二分图 最大匹配

来源:互联网 发布:直通车数据透视在哪里 编辑:程序博客网 时间:2024/05/01 18:38

hdu 2063 二分图—最大匹配

分类: 图论 287人阅读 评论(0)收藏 举报
ACM算法二分图最大匹配匈牙利算法

http://acm.hdu.edu.cn/showproblem.php?pid=2063

设G=(V,E)是一个无向图。如顶点集V可分区为两个互不相交的子集V1,V2之并,并且图中每条边依附的两个顶点都分属于这两个不同的子集。则称图G为二分图。二分图也可记为G=(V1,V2,E)。

给定一个二分图G,在G的一个子图M中,M的边集{E}中的任意两条边都不依附于同一个顶点,则称M是一个匹配。选择这样的子集中边数最大的子集称为图的最大匹配问题(maximal matching problem)

图的点覆盖:寻找一个点集,使得图中每一条边至少有一点在该点集中
二分图的最小点覆盖 = 最大匹配

图的独立集:寻找一个点集,其中任意两点在图中无对应边
一般图的最大独立集是NP完全问题
二分图的最大独立集 = 图的点数-最大匹配

图的路径覆盖:用不相交路径覆盖有向无环图的所有顶点
二分图的最小路径覆盖 = 节点数-最大匹配


匈牙利算法:增广路求最大匹配的方法

若P是图G中一条连通两个未匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。(M为一个匹配)

由增广路的定义可以推出下述三个结论:

  1-P的路径长度必定为奇数,第一条边和最后一条边都不属于M。

  2-将M和P进行异或操作(去同存异)可以得到一个更大的匹配M’。

  3-M为G的最大匹配当且仅当不存在M的增广路径。

算法描述:

  (1)置M为空

  (2)找出一条增广路径P,通过异或操作获得更大的匹配M’代替M

  (3)重复(2)操作直到找不出增广路径为止


题目大意:有n个女生和m个男生,以及K种可能的组合方式,求最多能有几对组合;

[cpp] view plaincopyprint?
  1. #include<stdio.h>
  2. #include<string.h>
  3. #define N 505
  4. int link[N],flag[N],map[1005][2],K;
  5. int Find(int x)
  6. {
  7. int i;
  8. for(i=0;i<K;i++){
  9. if(map[i][0]==x&&flag[map[i][1]]==0){
  10. flag[map[i][1]]=1; //标记访问过的点,防止出现死循环和重复计算
  11. if(link[map[i][1]]==0 || Find(link[map[i][1]])==1){
  12. link[map[i][1]]=x;
  13. return 1;
  14. }
  15. }
  16. }
  17. return 0;
  18. }
  19. int main()
  20. {
  21. int n,m,i,cnt;
  22. while(scanf("%d",&K),K){
  23. scanf("%d%d",&n,&m);
  24. memset(link,0,sizeof(link));
  25. for(i=0;i<K;i++){
  26. scanf("%d%d",&map[i][0],&map[i][1]);
  27. }
  28. for(i=1,cnt=0;i<=n;i++){ //每个点只需要查找一遍:若从某一点开始找不到增广路,
  29. memset(flag,0,sizeof(flag));//则无论当前匹配如何改变都无法再从该点找到增广路
  30. if(Find(i))
  31. cnt++;
  32. }
  33. printf("%d\n",cnt);
  34. }
  35. return 0;
  36. }

 

 

hdu 1498 二分图—最小点覆盖

分类: 图论 299人阅读 评论(0)收藏 举报
ACM算法二分图最小点覆盖

http://acm.hdu.edu.cn/showproblem.php?pid=1498

/*

题目大意:有 n*n 的矩阵中放着 1-50 种气球,每次只能毁掉一行或者一列。求 k 此操作后那些气球不能被全部毁掉。

解题思路:对于每一种给定的color,当 map[i][j] = color 时可以认为存在一条 i—j 的路径,这样就可以构建一个二分图,二分图的两个点集分别是该颜色气球的横纵坐标;毁掉该 colo r的所有气球所需的次数就是该二分图的最小点覆盖。

什么是图的点覆盖

*/

[cpp] view plaincopyprint?
  1. #include<stdio.h>
  2. #include<string.h>
  3. #define M 105
  4. #define N 55
  5. int map[M][M],link[M],flag[M],ans[N],n,k;//注意:link和flag数组长度应该和矩阵的行列数相等;
  6. int find(int i,int color)
  7. {
  8. int j;
  9. for(j=1;j<=n;j++){
  10. if(map[i][j]==color&&flag[j]==0){
  11. flag[j]=1;
  12. if(link[j]==0||find(link[j],color)==1){
  13. link[j]=i;
  14. return 1;
  15. }
  16. }
  17. }
  18. return 0;
  19. }
  20. int getnum(int color)
  21. {
  22. int i,sum;
  23. memset(link,0,sizeof(link));
  24. for(i=1,sum=0;i<=n;i++){
  25. memset(flag,0,sizeof(flag));
  26. if(find(i,color)==1)
  27. sum++;
  28. }
  29. return sum;
  30. }
  31. int main()
  32. {
  33. int i,j,t;
  34. while(scanf("%d%d",&n,&k),n||k){
  35. memset(map,0,sizeof(map));
  36. memset(ans,0,sizeof(ans));
  37. for(i=1;i<=n;i++){
  38. for(j=1;j<=n;j++){
  39. scanf("%d",&map[i][j]);
  40. }
  41. }
  42. for(i=1,t=0;i<=50;i++){ //注意这里是遍历每一种颜色,所以循环次数等于所有颜色数
  43. if(getnum(i)>k)
  44. ans[t++]=i;
  45. }
  46. if(t==0)
  47. printf("-1\n");
  48. else{
  49. for(i=0;i<t;i++){
  50. printf(i==0?"%d":" %d",ans[i]);
  51. }
  52. printf("\n");
  53. }
  54. }
  55. return 0;
  56. }

hdu 2255 二分图—最优匹配

分类: 图论 359人阅读 评论(0)收藏 举报
ACMKM算法二分图最优匹配图论

http://acm.hdu.edu.cn/showproblem.php?pid=2255

/*

最优匹配:使二分图的边权和最大的完备匹配。(如果二分图的两个点集不相等可以通过加 ’点‘ 和 ’权值为0的边‘ 实现转化)

相关概念
可行顶标:若L(x)是顶点x对应的顶标值。可行顶标对于图中的每条边(x,y)都有L(x)+L(y)>=w(x,y)
相等子图:只包含L(x)+L(y)=w(x,y)的边的子图

算法流程

设顶点Xi的顶标为a[i],顶点Yi的顶标为b[i]

ⅰ.初始时,a[i]为与Xi相关联的边的最大权值,b[j]=0,保证a[i]+b[j]>=w(i,j)成立

ⅱ.当相等子图中不包含完备匹配时,就适当修改顶标以扩大相等子图,直到找到完备匹配为止

修改顶标的方法:当从 Xi 开始寻找交错路失败后,得到一棵交错树,对交错树中 X 顶点的顶标减少 d 值, Y 顶点的顶标增加 d 值。
对于图中所有的边 (i,j) 有:

i和j都不在交错树中,边(i,j)仍然不属于相等子图
i和j都在交错树中,边(i,j)仍然属于相等子图
i不在交错树中,j在交错树中,a[i]+b[j]扩大,边(i,j)不属于相等子图
i在交错树,j不在交错树中,边(i,j)有可能加入到相等子图中

为了使a[i]+b[j]>=w(i,j)始终成立,且至少有一条边加入到相等子图中,d=min{a[i]+b[j]-w(i,j)},其中 i 在交错树中, j 不在交错树中。

由于在算法过程一直保持顶标的可行性,所以任意一个匹配的权值和肯定小于等于所有结点的顶标之和,又因为在扩大相等子图过程中优先加入了权值大大边,所以在相等子图中找到的第一个完备匹配就是最优匹配。
*/

题目大意:有n个人和n件物品,w[i][j]表示第 i 个人对第 j 件物品的出价,求这些物品能得到的最大价值;

[cpp] view plaincopyprint?
  1. #include<stdio.h>
  2. #include<string.h>
  3. #define N 305
  4. #define Max 1000000
  5. int w[N][N],linky[N];//数组 w 记录边权,数组 linky 记录顶点 y 的匹配;
  6. int lx[N],ly[N]; //数组 lx 和 ly 分别记录顶点 x 和 y 的顶标值;
  7. int visx[N],visy[N];//数组 visx 和 visy 分别记录顶点 x 和 y 是否被访问;
  8. int n,low; //low 记录每次顶标需要变化的值;
  9. int Find(int x)
  10. {
  11. int y,t;
  12. visx[x]=1;
  13. for(y=0;y<n;y++){
  14. if(visy[y])continue;
  15. t=lx[x]+ly[y]-w[x][y];
  16. if(t==0){
  17. visy[y]=1;
  18. if(linky[y]==-1||Find(linky[y])){
  19. linky[y]=x;
  20. return 1;
  21. }
  22. }
  23. else{
  24. if(low>t)low=t;
  25. }
  26. }
  27. return 0;
  28. }
  29. void KM()
  30. {
  31. int i,x;
  32. for(x=0;x<n;x++){
  33. while(1){
  34. memset(visx,0,sizeof(visx));
  35. memset(visy,0,sizeof(visy));
  36. low=Max;
  37. if(Find(x))break;
  38. for(i=0;i<n;i++){
  39. if(visx[i])
  40. lx[i]-=low;
  41. if(visy[i])
  42. ly[i]+=low;
  43. }
  44. }
  45. }
  46. }
  47. int main()
  48. {
  49. int i,j,sum;
  50. while(scanf("%d",&n)!=EOF){
  51. memset(lx,0,sizeof(lx));
  52. memset(ly,0,sizeof(ly));
  53. memset(linky,-1,sizeof(linky));
  54. for(i=0;i<n;i++){
  55. for(j=0;j<n;j++){
  56. scanf("%d",&w[i][j]);
  57. if(lx[i]<w[i][j])
  58. lx[i]=w[i][j];
  59. }
  60. }
  61. KM();
  62. for(i=0,sum=0;i<n;i++){
  63. sum+=w[linky[i]][i];
  64. }
  65. printf("%d\n",sum);
  66. }
  67. return 0;
  68. }

 

hdu 2444 二分图判定+最大匹配

分类: 图论 238人阅读 评论(0)收藏 举报
ACMC图论二分图判定最大匹配

http://acm.hdu.edu.cn/showproblem.php?pid=2444

题目大意:有n个人之间有m对互相认识,问能否将所有人分成两组,同一组的任意两人互不认识。若不能分组输出“No”,若能分组计算并输出有最多有多少对人互相认识;

思路:先用DFS进行染色,判断能否分成两组组(即能否构成二分图),如果可以求二分图的最大匹配;

[cpp] view plaincopyprint?
  1. #include<iostream>
  2. #include<stdio.h>
  3. #include<string.h>
  4. #include<stdlib.h>
  5. #define N 202
  6. using namespace std;
  7. int map[N][N],link[N],visit[N],color[N];
  8. bool flag;
  9. bool DFS(int i,int n,int c)
  10. {
  11. int j;
  12. for(j=1;j<=n;j++){
  13. if(map[i][j]==1){
  14. if(color[j]==c)
  15. continue;
  16. if(color[j]==0){
  17. color[j]=c;
  18. flag=DFS(j,n,-c);
  19. }
  20. else{
  21. return false;
  22. }
  23. if(!flag)
  24. return false;
  25. }
  26. }
  27. return true;
  28. }
  29. bool find(int x,int n)
  30. {
  31. int y;
  32. for(y=1;y<=n;y++){
  33. if(map[x][y]==1 && visit[y]==0){
  34. visit[y]=1;
  35. if(link[y]==-1 || find(link[y],n)){
  36. link[y]=x;
  37. return true;
  38. }
  39. }
  40. }
  41. return false;
  42. }
  43. int main()
  44. {
  45. int n,m,a,b,i,cnt;
  46. while(scanf("%d%d",&n,&m)!=EOF){
  47. flag=true;
  48. memset(map,0,sizeof(map));
  49. memset(link,-1,sizeof(link));
  50. memset(color,0,sizeof(color));
  51. while(m--){
  52. scanf("%d%d",&a,&b);
  53. map[a][b]=map[b][a]=1;
  54. }
  55. for(i=1;i<=n;i++){//对每个点通过DFS进行染色
  56. if(color[i]==0)
  57. color[i]=1;
  58. flag=DFS(i,n,-color[i]);
  59. if(!flag)break;
  60. }
  61. if(!flag){
  62. printf("No\n");
  63. continue;
  64. }
  65. for(i=1,cnt=0;i<=n;i++){
  66. memset(visit,0,sizeof(visit));
  67. if(find(i,n))
  68. cnt++;
  69. }
  70. printf("%d\n",cnt/2);
  71. }
  72. return 0;
  73. }

原创粉丝点击