正题
大意
求在一个扣掉m个格子的n*n的棋盘能放置的最多的马。
解题思路
求最大独立集就好了,最大独立集=点数-最大匹配数。最重要的是如何建图。定义一个数组point[i][j]表示点的编号。但是如果这样的话就会O(n4)O(n4)就会超时。现在我们把棋盘从左到右后从上到下标号,那这样奇数就攻击不到奇数,偶数就攻击不到偶数,然后分两边构图,就可以O(n4/2)O(n4/2)。
代码
#include<cstdio>
#include<cstring>
using namespace std;
int one,two,n,m,link[20001],ddx,ddy,w,s,zx[20001],zy[20001],point[201][201];
int tot,dx[8]={1,1,-1,-1,2,2,-2,-2},dy[8]={2,-2,2,-2,1,-1,1,-1};
bool a[201][201],cover[20001];
bool find(int i)//求最大匹配
{
int k=0;
for (int j=0;j<8;j++)
{
if (zx[i]+dx[j]<1 || zy[i]+dy[j]<1 || zx[i]+dx[j]>n || zy[i]+dy[j]>n) continue;
k=point[zx[i]+dx[j]][zy[i]+dy[j]];//记录
if (!a[zx[i]+dx[j]][zy[i]+dy[j]] && !cover[k])
{
int q=link[k];
link[k]=i;
cover[k]=true;
if (!q || find(q)) return true;
link[k]=q;
}
}
return false;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&ddx,&ddy);
a[ddx][ddy]=true;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
if (!a[i][j])
{
if ((i+j)%2==0) point[i][j]=++two;//标号
else
{
point[i][j]=++one;//标号
zx[one]=i;//记录坐标
zy[one]=j;
}
}
}
s=0;
for (int i=1;i<=one;i++)
{
memset(cover,0,sizeof(cover));
if (find(i)) s++;//找最大匹配数
}
printf("%d",n*n-m-s);
}