【二分图最大独立集】BZOJ4808[马]题解

来源:互联网 发布:哪个校音器软件好用 编辑:程序博客网 时间:2024/06/06 05:07

题目概述

给出 n×m 的棋盘(有些位置有损坏),问最多能在其中放多少互不吃到的马(不能放在损坏位置中)。

解题报告

NOIP2017前的最后一题QAQ。

首先将棋盘 01 间隔染色,然后就成了二分图。

由于要放最多的马,其实就是最大独立集。

最大独立集 = 点数 最小点覆盖 = 点数 最大匹配。

示例程序

#include<cstdio>using namespace std;const int maxn=200,maxm=200,maxt=20000;const int fl[8][2]={{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};int n,m,tot[2],ID[2][maxn+5][maxm+5];int E,lnk[maxt+5],nxt[maxt*8+5],son[maxt*8+5],who[maxt+5];int ti,vis[maxt+5],ans;#define check(x,y) (1<=(x)&&(x)<=n&&1<=(y)&&(y)<=m&&ID[0][x][y])#define Add(x,y) son[++E]=(y),nxt[E]=lnk[x],lnk[x]=Einline bool Find(int x){    if (vis[x]==ti) return false;vis[x]=ti;    for (int j=lnk[x];j;j=nxt[j])        if (!who[son[j]]||Find(who[son[j]])) return who[son[j]]=x,true;    return false;}int main(){    freopen("program.in","r",stdin);    freopen("program.out","w",stdout);    scanf("%d%d",&n,&m);    for (int i=1;i<=n;i++)    for (int j=1;j<=m;j++)    {        int x;scanf("%d",&x);if (x) continue;        ID[i+j&1][i][j]=++tot[i+j&1];    }    for (int i=1;i<=n;i++)    for (int j=1;j<=m;j++) if (i+j&1)    for (int f=0;f<8;f++) if (check(i+fl[f][0],j+fl[f][1]))        Add(ID[1][i][j],ID[0][i+fl[f][0]][j+fl[f][1]]);    for (int i=1;i<=tot[1];i++) ti++,ans+=Find(i);    return printf("%d\n",tot[0]+tot[1]-ans),0;}
原创粉丝点击