一张大小为n的国际象棋棋盘,上面有一些格子被拿走了,棋盘规模n不超过200。马的攻击方向如下图,其中S处为马位置,标有X的点为该马的攻击点。
你的任务是确定在这个棋盘上放置尽可能多的马,并使他们不互相攻击。
输入n,m,表示棋盘有n行,m个点在棋盘中被拿走了。
下面输入x,y为被拿走的点的坐标
输出能放置的最多的马的数量,使它们不互相攻击
3 2
1 1
3 3
5
首先为什么是求最大独立集?
对于本题来说,相互攻击的位置肯定不能同时存在两个马。
如果我们把两个相互攻击的位置连一条边,从而构成一个图。那么相邻
的两个点不能同时选,也就是求最大独立集。
考虑对棋盘进行黑白染色,想象一下国际象棋的棋盘。具体来说,设某个点的位置是i行j列,
那么当 [ ( i + j ) m o d 2 = = 0 ] [(i+j)mod2==0] [(i+j)mod2==0]时,该点为黑色,否则该点为白色。
一个很显然的性质,相互攻击的两个位置颜色一定不同。我们把黑点
放在左边,白点放在右边,那么这个图一定是二分图。
首先我们把点进行编号。设格子有n行m列,第 i i i行 j j j列的格子被编号为
( i − 1 ) ∗ m + j (i-1)*m+j (i−1)∗m+j。而对于被拿走的格子,我们完全可以认为这些点不存在,
不做任何处理。
染色之后我们用导航枚举8个方向进行连边操作,然后跑最大匹配(就是模板),用匈牙利算法。
最后的答案ans=顶点数-拿去的格子-最大匹配=9-2-2=5
#include
#include
#include
#include
typedef long long ll;
using namespace std;
int n,m,tot,ans,link[1000001],b[201][201],hd[1000001],cover[100001];
int dx[9]={0,1,1,-1,-1,2,2,-2,-2};//导航
int dy[9]={0,2,-2,2,-2,1,-1,1,-1};
struct node
{
int to,next;
}a[1000001];
void add(int x,int y)//邻接表不解释
{
a[++tot]=(node){y,hd[x]};
hd[x]=tot;
}
bool find(int x)//最大匹配模板
{
for(int i=hd[x];i>0;i=a[i].next)
{
int j=a[i].to;
if(cover[j]==0)
{
cover[j]=1;
int q=link[j];
link[j]=x;
if(q==0||find(q))
{
return true;
}
link[j]=q;
}
}
return false;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y;
cin>>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]==0)//是黑色格子且没有被破坏的格子
{
for(int k=1;k<=8;k++)//枚举8个方向分别与ta连边
{
int xx=i+dx[k];
int yy=j+dy[k];
if(xx>0&&xx<=n&&yy>0&&yy<=n)
{
if(!b[xx][yy])
{
add((i-1)*n+j,(xx-1)*n+yy);//连的是编号!编号=(i-1)*n+j
}
}
}
}
}
}
for(int i=1;i<=n*n;i++)
{
memset(cover,0,sizeof(cover));
find(i);
}
for(int i=1;i<=n*n;i++)
{
if(link[i]!=0)
{
ans++;
}
}
cout<<n*n-m-ans;//总的点-烂掉的点-最大匹配
return 0;
}