[最大独立集]Knights

题目描述

一张大小为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=顶点数-拿去的格子-最大匹配
}

猜你喜欢

转载自blog.csdn.net/weixin_43909855/article/details/85014774
今日推荐