题意
在一个n*n的矩阵中,放入尽可能多的马,使它们都不会互相攻击,这个矩阵中可能有几个块是会被挖掉的,那里就不能放马。
思路
这题就是求一个二分图的最大独立集。
定理:U=V-M;V=顶点数,M=最大匹配数(最小覆盖数),U=最大独立集。
这道题难点在于我们怎么去建图。首先我们把点数为奇数的和偶数的分别标开,因为点数为奇数的不会吃到点数为奇数的,所以这样不会矛盾,所以我们这样子做的话会减少之后找增广链的次数;然后,我们就从偶数点上去找增广链然后统计答案就好了。
代码
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,k[201][201],map[201][201],odd,eve,link[30001],vis[30001],ans,a,b,xa,ya;
struct node{
int x,y;
}c[30001];
short dx[9]={0,1,1,-1,-1,2,2,-2,-2},dy[9]={0,2,-2,2,-2,1,-1,1,-1};
int find(int x)
{
for (int i=1;i<=8;i++)
{
xa=c[x].x+dx[i];ya=c[x].y+dy[i];//找可以被吃掉的点
int j=map[xa][ya];//j为当前点能吃到的点的编号
if (!(xa<1||xa>n||ya<1||ya>n//判断范围
||(vis[map[xa][ya]]||k[xa][ya])))//没有访问过也没有被扣掉
{
int q=link[j];
link[j]=x;
vis[j]=1;
if (find(q)||!q) return 1;
link[j]=q;
}
}
return 0;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
k[a][b]=1;//k表示当前的方块是否被扣掉
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (!k[i][j])
{
if ((i+j)%2) map[i][j]=++odd;
else
{
map[i][j]=++eve;//记录编号
c[eve].x=i;
c[eve].y=j;//记录坐标
}
}
for (int i=1;i<=eve;i++)//从偶数点去找
{
memset(vis,0,sizeof(vis));
if (find(i)) ans++;//最大匹配数
}
printf("%d",n*n-m-ans);//最大独立集(要减去扣掉的点)
}