转自:http://blog.csdn.net/hackbuteer1/article/details/7398008
例题:
http://poj.org/problem?id=1469 COURSES
有了匈牙利算法的基础,该题就是一道非常简单的题目了:该题给出P门课程,N个学生,问能否从中选出P个学生,使每个学生上不同的课,且每个课程有一个学生。典型的二分图匹配的问题,我们只要计算最大二分图匹配数,如果和课程数相同就输出YES,否则输出NO。
//poj_1469 /*==================================================*\ | 二分图匹配(匈牙利算法DFS 实现) | INIT: g[][]邻接矩阵; | 优点:实现简洁容易理解,适用于稠密图,DFS找增广路快。 | 找一条增广路的复杂度为O(E),最多找V条增广路,故时间复杂度为O(VE) ==================================================*/ #include<stdio.h> #include<memory.h> bool g[110][310]; //邻接矩阵,true代表有边相连 bool flag,visit[310]; //记录V2中的某个点是否被搜索过 int match[310]; //记录与V2中的点匹配的点的编号 int p,n; //二分图中左边、右边集合中顶点的数目 // 匈牙利算法 bool dfs(int u) { for (int i = 1; i <= n; ++i) { if (g[u][i] && !visit[i]) //如果节点i与u相邻并且未被查找过 { visit[i] = true; //标记i为已查找过 if (match[i] == -1 || dfs(match[i])) //如果i未在前一个匹配M中,或者i在匹配M中,但是从与i相邻的节点出发可以有增广路径 { match[i] = u; //记录查找成功记录,更新匹配M(即“取反”) return true; //返回查找成功 } } } return false; } int main(void) { int i,j,k,t,v,ans; scanf("%d",&t); while (t--) { scanf("%d %d", &p, &n); for (i = 1; i <= p; i++) { for (j = 1; j <= n; j++) g[i][j] = false; } for (i = 1; i <= n; i++) match[i] = -1; flag = true; for (i = 1; i <= p; i++) { scanf("%d",&k); if (k == 0) flag = false; while (k--) { scanf("%d",&v); g[i][v] = true; } } if (flag) { ans = 0; for (i = 1; i <= p; i++) { memset(visit,false,sizeof(visit)); //清空上次搜索时的标记 if( dfs(i) ) //从节点i尝试扩展 ans++; } if (ans == p) puts("YES"); else puts("NO"); } else puts("NO"); } return 0; }
pku 2446 二分图最大匹配的应用
http://poj.org/problem?id=2446 Chessboard
题意:给出一个矩形N*M棋盘,有K个格子是空洞,然后用2*1的矩形,对所有非空洞的格子进行覆盖,如果可以全部覆盖,就puts("YES");
算法:建立二分图,用匈牙利算法;
我们分别对所有的格子进行标号1.。。N*M
将问题转化为二分图最大匹配问题。将棋盘按国际象棋棋盘那样添上黑白两种颜色,这样的话,黑色和白色的格子就构成了二分图的两个集合,即相邻的两个格子不会属于同个集合的。然后从上到下,从左到右对格子进行编号(除了洞),相邻的两格用边相连就构成一个二分图。然后求出最大匹配。。如果最大匹配+K=N*M就输出YES。。
//poj_2446 /*==================================================*\ | 二分图匹配(匈牙利算法DFS 实现) | INIT: g[][]邻接矩阵; | 优点:实现简洁容易理解,适用于稠密图,DFS找增广路快。 | 找一条增广路的复杂度为O(E),最多找V条增广路,故时间复杂度为O(VE) ==================================================*/ #include<stdio.h> #include<memory.h> #define MAX 1089 //33*33 bool g[MAX][MAX]; //邻接矩阵,true代表有边相连 bool flag,visit[MAX]; //记录V2中的某个点是否被搜索过 int match[MAX]; //记录与V2中的点匹配的点的编号 int cnt; //二分图中左边、右边集合中顶点的数目 bool hole[MAX][MAX]; int id[MAX][MAX]; // 匈牙利算法 bool dfs(int u) { for (int i = 1; i <= cnt; ++i) { if (g[u][i] && !visit[i]) //如果节点i与u相邻并且未被查找过 { visit[i] = true; //标记i为已查找过 if (match[i] == -1 || dfs(match[i])) //如果i未在前一个匹配M中,或者i在匹配M中,但是从与i相邻的节点出发可以有增广路径 { match[i] = u; //记录查找成功记录,更新匹配M(即“取反”) return true; //返回查找成功 } } } return false; } int MaxMatch() { int i,sum=0; memset(match,-1,sizeof(match)); for(i = 1 ; i <= cnt ; ++i) { memset(visit,false,sizeof(visit)); //清空上次搜索时的标记 if( dfs(i) ) //从节点i尝试扩展 { sum++; } } return sum; } int main(void) { int i,j,k,m,n,ans,y,x; while (scanf("%d %d %d",&m,&n,&k)!=EOF) { memset(g,false,sizeof(g)); memset(hole,false,sizeof(hole)); for (i = 1; i <= k; ++i) { scanf("%d %d",&y,&x); hole[x][y] = true; } if((m*n-k)&1) //奇偶剪枝 { puts("NO"); continue; } cnt = 0; for (i = 1; i <= m; ++i) { for (j = 1; j <= n; ++j) { if(hole[i][j] == false) //对没有涂黑的点进行标号 { id[i][j] = ++cnt; } } } for (i = 1; i <= m; ++i) { for (j = 1; j <= n; ++j) { if(hole[i][j] == false) { if(i-1>0 && hole[i-1][j] == false) //建图。。要注意边界问题 g[ id[i][j] ][ id[i-1][j] ] = true; if(i+1<=m && hole[i+1][j] == false) g[ id[i][j] ][ id[i+1][j] ] = true; if(j-1>0 && hole[i][j-1] == false) g[ id[i][j] ][ id[i][j-1] ] = true; if(j+1<=n && hole[i][j+1] == false) g[ id[i][j] ][ id[i][j+1] ] = true; } } } ans = MaxMatch(); if (ans == cnt) puts("YES"); else puts("NO"); } return 0; }