【ssl1344】Knight【二分图匹配】【最大独立集】

48 篇文章 0 订阅
13 篇文章 0 订阅

Description

一张大小为n的国际象棋棋盘,上面有一些格子被拿走了,棋盘规模n不超过200。马的攻击方向如下图,其中S处为马位置,标有X的点为该马的攻击点。

你的任务是确定在这个棋盘上放置尽可能多的马,并使他们不互相攻击。

Input

输入n,m,表示棋盘有n行,m个点在棋盘中被拿走了。
下面输入x,y为被拿走的点的坐标

Output

输出能放置的最多的马的数量,使它们不互相攻击

Sample Input

3 2
1 1
3 3

Sample Output

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 (i1)m+j。而对于被拿走的格子,我们完全可以认为这些点不存在,
不做任何处理。

染色之后我们用导航枚举8个方向进行连边操作,然后跑最大匹配(就是模板),用匈牙利算法。

最后的答案ans=顶点数-拿去的格子-最大匹配=9-2-2=5

上代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值