Chessboard - POJ 2446 - Virtual Judge (csgrandeur.cn)
要不是在做专项,头一次看到这个题完全想不到是二分图匹配的做法,看了大佬的博客发现二分图匹配很多时候是需要自己去构造或者是抽象出来的,就比如这道题,它完全就是靠题目条件“用1*2的卡片去覆盖“来抽象出来的一个二分图。其二分关系就是一点与周围相邻的点可以组合用一块卡片覆盖,就相当于是在传统二分图中的路径。
由dfs将相邻的不是洞的点遍历一遍建立路径关系。
跑一边朴素的匈牙利算法即可。需要注意,因为是点与点的对应关系,而点又是二维,所以要想和传统二分图匹配保持一致,就需要进行二维变一维的转化。
最后题目需要判断是否是完全匹配,即除了洞之外的所有点都要被覆盖,即(n*m-k)/2个卡片,但是根据匈牙利算法的原理,加上我们建的图是无向图,所以做后得出的匹配数是卡片数的二倍(可以联想成一张卡片头尾互换)。故如果最后的匹配数等于n*m-k就YES,否则就NO。
(小坑,输入的坐标是(y,x),纵坐标在前的。)
代码如下:
//#include<bits/stdc++.h>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
//
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f
#define ll long long
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
int hole[40][40];
int mp[2005][2005];//需要开到转化后的范围
int vis[2005];
int match[2005];
int n,m,k;
int js(int x,int y)//二维化一维,将横坐标乘上纵坐标的总数加上纵坐标,这样可以保证一对一转化
{
return (x-1)*m+y;
}
void dfs(int x,int y)//建立路径关系
{
for(int i=0;i<4;i++)
{
int xx=x+dx[i],yy=y+dy[i];
if(xx>=1&&yy>=1&&xx<=n&&yy<=m&&!hole[xx][yy])
mp[js(x,y)][js(xx,yy)]=1;
}
}
bool find(int x)//朴素的匈牙利算法
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mp[x][js(i,j)]&&!vis[js(i,j)])
{
vis[js(i,j)]=1;
if(!match[js(i,j)]||find(match[js(i,j)]))
{
match[js(i,j)]=x;
return true;
}
}
}
}
return false;
}
int main()
{
IOS;
cin>>n>>m>>k;
for(int i=1;i<=k;i++)
{
int x,y;
cin>>x>>y;
hole[y][x]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(!hole[i][j])
dfs(i,j);
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
memset(vis,0,sizeof vis);
if(find(js(i,j)))
ans++;
}
}
if(ans==n*m-k)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
return 0;
}