二分图最大匹配(匈牙利算法)整理

 近期做了两个二分图的题,之前一直不会,最近就学习了一下匈牙利算法:

匈牙利算法是用来解决有关二分图匹配问题的算法。

首先,先了解什么是二分图:就是顶点集V可分割为两个互不相交的子集,并且图中每条边依附的两个顶点都分属于这两个互不相交的子集,两个子集内的顶点不相邻。

就是u中的点只能与v中的点相连,并且v中的点只能和u中的点相连。


匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名。匈牙利算法是基于Hall定理中充分性证明的思想,

它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。

匈牙利算法就是去找其中一个集合里每一个点是否可以匹配另一个集合中未被匹配的点,若当前点无法匹配就去找前边已经找过的点的

点是否可以腾出来一个点给当前的点,如果可以返回1,否则返回0.

这里推荐两个博客:

http://blog.csdn.net/dark_scope/article/details/8880547    这个写的比较有趣。





poj 2446 

Chessboard

 

Alice and Bob often play games on chessboard. One day, Alice draws a board with size M * N. She wants Bob to use a lot of cards with size 1 * 2 to cover the board. However, she thinks it too easy to bob, so she makes some holes on the board (as shown in the figure below). 

We call a grid, which doesn’t contain a hole, a normal grid. Bob has to follow the rules below: 
1. Any normal grid should be covered with exactly one card. 
2. One card should cover exactly 2 normal adjacent grids. 

Some examples are given in the figures below: 
 
A VALID solution.

 
An invalid solution, because the hole of red color is covered with a card.

 
An invalid solution, because there exists a grid, which is not covered.

Your task is to help Bob to decide whether or not the chessboard can be covered according to the rules above.
Input
There are 3 integers in the first line: m, n, k (0 < m, n <= 32, 0 <= K < m * n), the number of rows, column and holes. In the next k lines, there is a pair of integers (x, y) in each line, which represents a hole in the y-th row, the x-th column.
Output
If the board can be covered, output "YES". Otherwise, output "NO".
Sample Input
4 3 2
2 1
3 3
Sample Output
YES
Hint
 
A possible solution for the sample input.


题目的意思是:给一个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    多米诺

题目描述 Description

一个矩形可以划分成M*N个小正方形,其中有一些小正方形不能使用。一个多米诺骨牌占用两个相邻的小正方形。试问整个区域内最多可以不重叠地放多少个多米诺骨牌且不占用任何一个被标记为无法使用的小正方形。

输入描述 Input Description

第一行有两个用空格隔开的正整数MN

    第二行有一个正整数K,表示共有K个小正方形不能使用。输入数据保证K<=M*N

    以下K行每行有两个用空格隔开的数XY,表示第X行的第Y个小正方形不能使用。

输出描述 Output Description

输出最多能放多少个多米诺骨牌。

样例输入 Sample Input

3 3

2

1 1

2 2

样例输出 Sample Output

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;
}





你可能感兴趣的:(匈牙利算法)