对于本题来说,相互攻击的位置肯定不能同时存在两个马。
如果我们把两个相互攻击的位置连一条边,从而构成一个图。
那么相邻的两个点不能同时选,也就是求最大独立集。
所以,我们可以把此题转化成最大匹配问题。
这道题我们需要黑白点覆盖,因为这是国际象棋,黑点只能跳白点,白点只能跳黑点。
我们考虑黑点连白点建立一个二分图,
如何建?如果当前点是黑点,就让当前点分八个点拓展到其它点并建边 。
用不了坐标建边,就用编号建边。推一推可得点的编号为: ( i − 1 ) ∗ n + j (i-1)*n+j (i−1)∗n+j 。
然后正常匈牙利跑最大匹配即可。
当然,由于是求最大独立集,所以:
答案 = = = 顶点数 − - − 拿去的格子 − - − 最大匹配。
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int dx[9]={
0,1,1,-1,-1,2,2,-2,-2};
int dy[9]={
0,2,-2,2,-2,1,-1,1,-1};
int b[3010][3010],a[3010][3010],v[40200],link[1000010],ls[1000010];
int n,m,x,y,tot,ans;
struct node
{
int y,next;
}map[1000010];
void add(int x,int y)
{
map[++tot].y=y;
map[tot].next=ls[x];
ls[x]=tot;
}
int dfs(int x) //找最大匹配
{
for(int i=ls[x]; i; i=map[i].next)
{
int y=map[i].y;
if(!v[y])
{
v[y]=1;
int fa=link[y];
link[y]=x;
if(fa==0||dfs(fa))
return 1;
link[y]=fa;
}
}
return 0;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
scanf("%d%d",&x,&y);
b[x][y]=1;
}
for(int i=1; i<=n; i++) //枚举每个点
for(int j=1; j<=n; j++)
if((i+j)%2==0&&!b[i][j]) //是黑点且格子没被拿走
{
for(int k=1; k<=8; k++) //八个方向
{
int xx=i+dx[k];
int yy=j+dy[k];
if(xx>=1&&xx<=n&&yy>=1&&yy<=n)
if(!b[xx][yy])
add((i-1)*n+j,(xx-1)*n+yy);
}
}
for(int i=1; i<=n*n; i++)
{
memset(v,0,sizeof(v));
if(dfs(i))
ans++;
}
printf("%d",n*n-m-ans);
return 0;
}