CH 6801 棋盘覆盖 匈牙利算法

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/88811857

title

CH 6801
描述

给定一个N行N列的棋盘,已知某些格子禁止放置。求最多能往棋盘上放多少块的长度为2、宽度为1的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。N≤100。

输入格式

第一行为n,t(表示有t个删除的格子)
第二行到t+1行为x,y,分别表示删除格子所在的位置
x为第x行,y为第y列,行列编号从1开始。

输出格式

一个数,即最多能放的骨牌数

样例输入

8 0

样例输出

32

analysis

虽说《算法竞赛进阶指南》几乎没有裸题,但这题快可以算一道了。

这题我们设下标x,y的和是奇数的点为奇点,否则就是偶点。

如果相邻的两个点都可以放骨牌,那么我们就见一条从奇点出发(或从偶点)到另一个点的一个边。

然后这个棋盘就成了一个二分图,奇点和偶点为两个集合,我们选择尽量多的边,但两边之间没有公共点(因为一个点上不能放两块骨牌)。

根据二分图匹配……不用根据了,这就是一个求二分图最大匹配的问题。

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
const int dic[4][2]={{-1,0},{0,-1},{0,1},{1,0}};
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
	ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int match[maxn];
bool vis[maxn];
inline bool dfs(int x)
{
	for (int i=head[x]; i; i=Next[i])
	{
		int y=ver[i];
		if (!vis[y])
		{
			vis[y]=1;
			if (!match[y] || dfs(match[y]))
			{
				match[y]=x;
				return true;
			}
		}
	}
	return false;
}
int n,t;
inline int getnum(int x,int y)
{
	return (x-1)*n+y;
}
int del[maxn][maxn];
int main()
{
	read(n);read(t);
	for (int i=1; i<=t; ++i)
	{
		int x,y;
		read(x);read(y);
		del[x][y]=1;
	}
	for (int i=1; i<=n; ++i)
		for (int j=1; j<=n; ++j)
			if (!del[i][j])
				for (int k=0; k<=3; ++k)
				{
					int x=i+dic[k][0],y=j+dic[k][1];
					if (x>0 && y>0 && x<=n && y<=n && !del[x][y] && (x+y)%2)
						add(getnum(i,j),getnum(x,y));
				}
	int ans=0;
	for (int i=1; i<=getnum(n,n); ++i)
	{
		memset(vis,0,sizeof(vis));
		if (dfs(i)) ++ans;
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/88811857