近期做了两个二分图的题,之前一直不会,最近就学习了一下匈牙利算法:
匈牙利算法是用来解决有关二分图匹配问题的算法。
首先,先了解什么是二分图:就是顶点集V可分割为两个互不相交的子集,并且图中每条边依附的两个顶点都分属于这两个互不相交的子集,两个子集内的顶点不相邻。
就是u中的点只能与v中的点相连,并且v中的点只能和u中的点相连。
匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名。匈牙利算法是基于Hall定理中充分性证明的思想,
它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。
匈牙利算法就是去找其中一个集合里每一个点是否可以匹配另一个集合中未被匹配的点,若当前点无法匹配就去找前边已经找过的点的
点是否可以腾出来一个点给当前的点,如果可以返回1,否则返回0.
这里推荐两个博客:
http://blog.csdn.net/dark_scope/article/details/8880547 这个写的比较有趣。
poj 2446
4 3 2 2 1 3 3Sample Output
YESHint
题目的意思是:给一个m*n的区域,这个m*n区域内有k个位置是不能用的,向其中放大小为1*2的小矩形,放完后能刚好放完输出yes,否则输出no。
将这个m*n区域按棋盘那样黑色和白色分开可以构成一个二分图,当一个点的横坐标加上纵坐标为奇数时,它的上下左右的坐标和一定为偶数。就按照这个规律将图二分。
#include
#include
#include
#include
using namespace std;
int map[2550][2550],vis[2550],line[2550][2550],pre[2550],m,n,k,t1,t2;
int tx[4]={1,-1,0,0};
int ty[4]={0,0,-1,1};
int find(int x){
for(int i=1;i<=t2;i++){
if(!vis[i]&&line[x][i]){
vis[i]=1;
if(pre[i]==-1||find(pre[i])){
pre[i]=x;
return 1;
}
}
}
return 0;
}
int main()
{
while(~scanf("%d%d%d",&m,&n,&k)){
memset(map,0,sizeof(map));
memset(pre,-1,sizeof(pre));
memset(line,0,sizeof(line));
int a,b;
t1=0,t2=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++){
if((i+j-1)%2)
map[i][j]=++t1;
else
map[i][j]=++t2;
}
for(int i=1;i<=k;i++){
scanf("%d%d",&a,&b);
map[b][a]=-1;
}
if(n*m%2!=k%2){
printf("NO\n");
continue;
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(map[i][j]!=-1&&(i+j-1)%2){
for(int l=0;l<4;l++){
int xx=i+tx[l];
int yy=j+ty[l];
if(xx>=1&&xx<=m&&yy>=1&&yy<=n&&map[xx][yy]!=-1){
line[map[i][j]][map[xx][yy]]=1;
}
}
}
}
}
int ans=0;
for(int i=1;i<=t1;i++){
memset(vis,0,sizeof(vis));
if(find(i))
ans++;
}
if(ans*2==n*m-k)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
codevs 3062 多米诺
一个矩形可以划分成M*N个小正方形,其中有一些小正方形不能使用。一个多米诺骨牌占用两个相邻的小正方形。试问整个区域内最多可以不重叠地放多少个多米诺骨牌且不占用任何一个被标记为无法使用的小正方形。
第一行有两个用空格隔开的正整数M和N。
第二行有一个正整数K,表示共有K个小正方形不能使用。输入数据保证K<=M*N。
以下K行每行有两个用空格隔开的数X和Y,表示第X行的第Y个小正方形不能使用。
输出最多能放多少个多米诺骨牌。
3 3
2
1 1
2 2
3
#include
#include
#include
#include
using namespace std;
int map[2550][2550],vis[2550],line[2550][2550],pre[2550],m,n,k,t1,t2;
int tx[4]={1,-1,0,0};
int ty[4]={0,0,-1,1};
int find(int x){
for(int i=1;i<=t2;i++){
if(!vis[i]&&line[x][i]){
vis[i]=1;
if(pre[i]==-1||find(pre[i])){
pre[i]=x;
return 1;
}
}
}
return 0;
}
int main()
{
while(~scanf("%d%d%d",&m,&n,&k)){
memset(map,0,sizeof(map));
memset(pre,-1,sizeof(pre));
memset(line,0,sizeof(line));
int a,b;
t1=0,t2=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++){
if((i+j-1)%2)
map[i][j]=++t1;
else
map[i][j]=++t2;
}
for(int i=1;i<=k;i++){
scanf("%d%d",&a,&b);
map[a][b]=-1;
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(map[i][j]!=-1&&(i+j-1)%2){
for(int l=0;l<4;l++){
int xx=i+tx[l];
int yy=j+ty[l];
if(xx>=1&&xx<=m&&yy>=1&&yy<=n&&map[xx][yy]!=-1){
line[map[i][j]][map[xx][yy]]=1;
}
}
}
}
}
int ans=0;
for(int i=1;i<=t1;i++){
memset(vis,0,sizeof(vis));
if(find(i))
ans++;
}
printf("%d\n",ans);
}
return 0;
}