题目描述
一张大小为n*n的国际象棋棋盘,上面有一些格子被拿走了,棋盘规模n不超过200。马的攻击方向如下图,其中S处为马位置,标有X的点为该马的攻击点。
你的任务是确定在这个棋盘上放置尽可能多的马,并使他们不互相攻击。
输入输出格式
输入格式
输入的第一行包含两个整数n和m,由一个空格分隔,1<=n<=200,0<=m<n2;n是棋盘大小,m是移除的格子数。以下m行中的每一行包含两个整数:x和y,由一个空格分隔,1<=x,y<=n——这些是移除格子的坐标。棋盘的左上角的坐标是(1,1),右下角的坐标是(n,n)。不重复拿走的格子。
输出格式
输出应该包含一个整数。它应该是放置最大的马的数量,可以放置在给定的棋盘上并且没有互相攻击。
输入输出样例
输入样例#1:
3 2
1 1
3 3
输出样例#1:
5
题目解析
对于本题来说,相互攻击的位置肯定不能同时存在两个马。
如果我们把两个相互攻击的位置连一条边,从而构成一个图。
那么相邻的两个点不能同时选,也就是求最大独立集。
ans=顶点数-拿去的格子-最大匹配
建模
考虑对棋盘进行黑白染色。
那么当[(i+j)%2==0]时,该点为黑色,否则该点为白色。
一个很显然的性质,相互攻击的两个位置颜色一定不同。我们把黑点放在左边,白点放在右边,那么这个图一定是二分图。
首先我们把点进行编号。第i行j列的格子被编号为(i-1)*m+j。
因为连的是无向图,所以我们只从黑点出发连向白点
代码
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
long long n,m,u,v,ans,link[40005],s[205][205],p,maxn;
vector<int> a[40005];
bool flag[40005],k[205][205];
int fx[8][2]={{1,2},{1,-2},{2,1},{2,-1},{-1,2},{-1,-2},{-2,1},{-2,-1}};//马的8个方向
bool find(int x)
{
for(int i=0;i<a[x].size();i++)
if(!flag[a[x][i]])
{
int j=a[x][i];
flag[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<=n;i++)
for(int j=1;j<=n;j++)
s[i][j]=n*(i-1)+j;//求每个点位置的编号
for(int i=1;i<=m;i++)
{
cin>>u>>v;
k[u][v]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!k[i][j]&&(i+j)%2==0)//判断是否被移除或是黑点
{
int ax,ay;
for(int kk=0;kk<8;kk++)//8个方向
{
ax=i+fx[kk][0],ay=j+fx[kk][1];
if(ax<1||ax>n||ay<1||ay>n) continue;//判断是否越界
if(k[ax][ay]) continue;//判断该可攻击的点是否被移除
a[s[i][j]].push_back(s[ax][ay]);//连边
}
}
for(int i=1;i<=n*n;i++)
{
memset(flag,0,sizeof(flag));
maxn+=find(i);
}
cout<<n*n-maxn-m;// ans=顶点数-拿去的格子-最大匹配
}