题目概述
给出 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]=E
inline 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;
}