棋盘覆盖
给定一个 N 行 N 列的棋盘,已知某些格子禁止放置。
求最多能往棋盘上放多少块的长度为 2、宽度为 1 的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。
输入格式
第一行包含两个整数 N 和 t,其中 t 为禁止放置的格子的数量。
接下来 t 行每行包含两个整数 x 和 y,表示位于第 x 行第 y 列的格子禁止放置,行列数从 1 开始。
输出格式
输出一个整数,表示结果。
数据范围
1≤N≤100,
0≤t≤100
输入样例:
8 0
输出样例:
32
题目分析:这题乍一看是状压DP,但是题目数据范围是100比较大,所有==所以要考虑别的思路,由于卡片只能放到相邻的两个格子当中,我们把每个格子看作一个点,相邻两个格子连出一条边,于是这个题就抽象成了最多选多少条边,所有选出的边之间没有公共点,这就是最大匹配问题。
就比如下面这个图,黑色区域是禁止放置的
经过匹配之后:
求最大匹配问题可以用匈牙利算法求解,但是用匈牙利算法前提需要图是二分图,所以我们需要判断一下是不是二分图。
一个n*n的矩阵,我们通过染色把黑色区域看作一个集合,白色区域看作一个集合。
两个集合当中每个点相邻的点的颜色都是不同的,通过染色法判定我们发现这就是一个二分图。而且黑色区域每个点坐标和为偶数,白色区域每个点坐标和为奇数,因此我们就可以用匈牙利算法进行求解最大匹配问题。
实现步骤:
1.建图(标记禁止放置的点)
2.对二分图中的黑色区域集合进行匹配并且更新答案
3.输出最大匹配数
具体ACcode:
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int ,int> PII;
typedef long long ll;
const int N = 110;
int n,m;
PII match[N][N];//标记匹配点
int g[N][N];//存棋盘上坏的点
int st[N][N];//标记是否匹配过
int dx[4]={
-1,0,1,0},dy[4]={
0,1,0,-1};//四个方向匹配
bool find(int x,int y)
{
for(int i=0;i<4;i++)
{
int nx=x+dx[i],ny=y+dy[i];
if(nx<1||nx>n||ny<1||ny>n)continue;//不能超出棋盘
if(st[nx][ny]||g[nx][ny])continue;//不能匹配过或者是禁止放置的点
st[nx][ny]=1;//标记匹配过
PII t=match[nx][ny];
if(t.x==0||find(t.x,t.y))
//如果未被标记或者前一个匹配点还可以找到另外一个人匹配
{
match[nx][ny]={
x,y};//更新匹配点
return true;
}
}
return false;
}
int main()
{
cin>>n>>m;
while(m--)
{
int x,y;
cin>>x>>y;
g[x][y]=1;//标记禁止放置的点
}
int res=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if((i+j)%2&&!g[i][j])//对二分图中的黑色区域集合进行匹配
{
memset(st,0,sizeof st);
if(find(i,j))res++;
}
}
cout<<res<<endl;
return 0;
}