【学校OJ】二分图匹配 骑士共存问题

题目描述

一个N*N的棋盘上,有一些小方格被拿走了,不能放置骑士,其它位置可以放。现要在棋盘上放若干骑士,要求任一个骑士都不能在其他骑士的攻击点上。请算出棋盘上最多能有几个骑士。骑士攻击范围如图所示(S是骑士的位置,X表示马的攻击点) 

输入

第一行包含2个整数n和m,用单个的空格分开,1<=n<=200 , 0<=m < 40000;n 是国际象棋棋盘的大小,m是被拿走的格子数。 下面m行每行包含 2 个整数:x和y,用单个的空格分开,1<=x,y<=n,这些是被拿走的格子的坐标。 棋盘的左上角的坐标是(1,1),右下角是(n,n)。拿走的格子没有重复的。

输出

一个整数,它应该是能放在国际象棋棋盘上的互不攻击对方的马的最大的数量。

样例输入

Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

3 2
1 1
3 3

样例输出

5

    由于没有绑定手机而苦逼了很久,所以为了补偿就来一篇吧。

    二分图呢,就是这样一种图:可以把所有节点分成两个部分,x与y,而所有边必须是x连接到y,不允许x或y中出现自己连接的情况。而由于二分图的这种性质也是定义,就出现了一种问题——二分图的最大匹配。就是求二分图中选出尽可能多的边,让任意两条边不相连。一种通俗的诠释:一群剩男剩女进行组成伴侣,在不同性恋、不一夫多妻、一妻多夫的情况下撮合尽可能多的情侣。

    匈牙利算法就可以解决这种问题。具体操作是这样的:

    1.寻找一个没有匹配的x节点(剩男),然后从它所有有边相连的(互相有感觉的)y节点中寻找一个同样没有匹配的(剩女),这时,将他们加入到匹配里面(撮合成一对),任务完成(撮合成功),再进行一遍1,直到无法进行1为止(没有剩男了~)。如果有边相连的y全部都在匹配中(没有两厢情愿的女孩子是单身),则对每一个相连的y进行2

    2.将这个有边相连的(有男朋友的)y的x(男朋友)进行待定,如果x可以找到除了自己的其他空闲的y(有其他剩女可以选),那么将匹配关系变一变(小三上位,原配另找新欢),就多了一对匹配不是么?但如果同样没有找到怎么办?一样对所有相连的y进行2操作,直到多了一对匹配为止~如果找遍所有人都无法解决问题,很抱歉原来的这位x,你永远没有办法匹配(一辈子单身,惨)了……

    咳咳咳,通过黑色和白色字的讲解,可以大致了解匈牙利算法了。那么来看看这道题吧。

    我们可以显而易见的看出,按国际象棋格子的分法,有黑色和白色之分,可以很容易的发现,马从黑色只能跳到白色,白色只能跳到黑色,也就是处于白色格子的没有自区域的相连,黑的同样没有,符合二分图的性质,而一步可以跳到的地方就相当于有一条边相连。

    有边相连的格点不可以同时放马,所以放了一个马与之相连的所有边就全部被控制。删去最少点使得所有边都不会冲突,这就是最小点覆盖边问题!答案是等于最大匹配的!然后用总数减去最大匹配数即可了!

    当然,有了思路,要想不超时,就看你的代码丑不丑了……

#include
#include
#include
using namespace std;
int getint()
{
	int p=0,f=0;
	char c=getchar();
	while((c<'0'||c>'9')&&c!='-')c=getchar();
	if(c=='-')
		f=1,c=getchar();
	while(c>='0'&&c<='9')
		p=p*10+c-'0',c=getchar();
	return f?-p:p;
}
void putint(int p)
{
	if(p<0)putchar('-'),p=-p;
	if(p>9)putint(p/10);
	putchar(p%10+'0');
}
int n,m,sum,cnt[2];
bool A[205][205];
int num[205][205];
int d[8][2]={{2,1},{-2,1},{2,-1},{-2,-1},{1,2},{-1,2},{1,-2},{-1,-2}};
int to[20005][10];
int CP[20005];
bool vis[20005];
bool check(int x,int y)
{
	if(x<1||y<1||x>n||y>n||A[x][y]||vis[num[x][y]])return 0;
	return 1;
}
bool xyl(int x)
{
	for(int i=1;i<=to[x][0];i++)
	{
		int p=to[x][i];
		if(!vis[p])
		{
			vis[p]=1;
			if(!CP[p]||xyl(CP[p]))
			{
				CP[p]=x;
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
	n=getint();m=getint();
	for(int i=1;i<=m;i++)
	{
		int a=getint(),b=getint();
		A[a][b]=1;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(!A[i][j])
				num[i][j]=++cnt[(i+j)%2];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(!A[i][j]&&(i+j)%2==0)
				for(int k=0;k<8;k++)
					if(check(i+d[k][0],j+d[k][1]))
						to[num[i][j]][++to[num[i][j]][0]]=num[i+d[k][0]][j+d[k][1]];
	for(int i=1;i<=cnt[0];i++)
	{
		sum+=xyl(i);
		memset(vis,0,sizeof(vis));
	}
	putint(cnt[1]+cnt[0]-sum);
}


你可能感兴趣的:(-->图论强行套模板<--,图论,--,二分图)