二分图 匈牙利算法

概念:(二分图中)

      1. 最大匹配数: 任意两条边不相交于同一顶点的边数。

      2. 最小覆盖:  用最少的点让每一条边都至少和其中的一个点相关联。(不大于图中的小的一个点集数) 且点数=最大匹配数

      3. 最大独立集: 两两之间没有边的点的最大数量。(最小为图中大的一个点集数) 点数=总点数-最大匹配数

      4. 最小路径覆盖: 用最小不相交的路径覆盖有向无环图的所有节点。

 

很多题目将模型抽象出来就是简单的二分图。

 

eg. poj 2239  selecting courses

           一周的课12节,有n门课,每门课一周有多个时间,教授相同的内容。求一个学生一周最多课上几门课。(多门课上课的时间不能冲)

           将课程和时间简化成二分图的两个点集,即求该二分图的最大匹配数。

    hdu 1068  girls and boys

           男孩和女孩都有不同的心仪的对象,求最多能找到多少人一个集合,是男孩女孩彼此都不心仪。

           将男孩和女孩简化成二分图的两个点集(不分开,即两个点集相同),彼此之间的心仪关系用边来表示,此题即是求该二分图的最大独立点集。

    poj 1325  Machine Schedule

           两台机器,有多个运行的状态。每个进程在两台机器上所需的运行状态都不相同(进程在两台机器上的一台完成即可)。而且机器每更新一次状态都要重启一次,求完成所有进程需要重启机器的最小次数。

           将两台机器的状态数分别简化成二分图的两个点集,一个进程在两个机器上所需要的不同状态用边来表示。那么所求的就是该二分图的最小覆盖。

    poj 1511  Air Raid

           小镇有n个路口,两个路口之间是单行道。街道彼此不交叉;有空兵降落到此镇的路口。求怎样降落使空兵数最少且能走遍整个小镇。

           将小镇的路口置为二分图的两个点集(相同),用便表示彼此之间存在街道。此题即是求该二分图的最小边覆盖。

 

 

 匈牙利算法即时用来求最大匹配数;即构造增广路径。

           增广路径: 连接两个未匹配的节点间的路径,且节点间的边是已匹配的和未匹配的交替存在。

           最大匹配数: 从一个集合的顶点开始遍历,依次判断该点到另一个集合的某个点是否存在增广路径,存在则匹配数加1.

 

 

一般二分图的问题可以转化成网络图中的最大流问题来做,只需要将两个顶点集的旁边增加一个超级起点和一个超级终点即可,这样就构造出了网络流图,再利用要求的性质将流量加上,即可将二分图中的最大匹配数的求法转化为网络流图中的最大流问题。

 

代码:

  

          poj 2239:

                 

#include<iostream>
#include<string.h>
using namespace std;
int n;
int a[300][90];
int AG[300];
int exist[300];
int able(int now){
    for(int j=1;j<=90;j++){
            if(exist[j]==0&&a[now][j]>0){
                     exist[j]=1;
                     int temp=AG[j];
                     if(temp==0||able(temp)==1){
                             AG[j]=now;
                             return 1;
                             }
                             }
                             }    
    return 0;
}
int find(){
     memset(AG,0,sizeof(AG));
     int sum=0;
     for(int i=1;i<=n;i++){
             memset(exist,0,sizeof(exist));
             if(able(i)==1){
                  sum++;
  //                cout<<sum<<" "<<i<<endl;
                  }
                  }
     return sum;
     }

int main(){
    while(cin>>n){
        //memset(a,0,sizeof(0));
        for(int g=0;g<=n;g++){
                for(int f=0;f<=90;f++){
                        a[g][f]=0;
                        }
                }
        for(int k=1;k<=n;k++){
                int times;
                cin>>times;
                for(int h=1;h<=times;h++){
                        int temp1,temp2;
                        cin>>temp1>>temp2;
                        a[k][(temp1-1)*12+temp2]=1;
                        }
                        }
        
                cout<<find()<<endl;
                }
    return 0;
}

 

 

     hdu 1068

              

#include<iostream>
#include<string.h>
using namespace std;
int n;
int a[1000][1000];
int AG[1000];
int exist[1000];
int able(int now){
    for(int j=0;j<n;j++){
            if(exist[j]==0&&a[now][j]>0){
                     exist[j]=1;
                     int temp=AG[j];
                     if(temp==0||able(temp)==1){
                             AG[j]=now;
                             return 1;
                             }
                             }
                             }    
    return 0;
}
int find(){
     memset(AG,0,sizeof(AG));
     int sum=0;
     for(int i=0;i<n;i++){
             memset(exist,0,sizeof(exist));
             if(able(i)==1){
                  sum++;
  ///               cout<<sum<<" "<<i<<endl;
                  }
                  }
     return sum;
     }

int main(){
    while(cin>>n){
        //memset(a,0,sizeof(0));
        for(int g=0;g<=n;g++){
                for(int f=0;f<=n;f++){
                        a[g][f]=0;
                        }
                }
        for(int k=0;k<n;k++){
                int times,temp2;
                scanf("%d: (%d)",&temp2,&times);
               // cin>>times;
                for(int h=1;h<=times;h++){
                        int temp1;
                        cin>>temp1;
                        a[temp2][temp1]=1;
                        }
                        }
        
                cout<<n-find()/2<<endl;
                }
    return 0;
}

 

    

        poj 1325

           

#include<iostream>
#include<string.h>
using namespace std;
int n,m;
int a[100][100];
int AG[100];
int exist[100];
int flag1,flag2;
int able(int now){
    for(int j=0;j<m;j++){
            if(exist[j]==0&&a[now][j]>0){
                     exist[j]=1;
                     int temp=AG[j];
                     if(temp==-1||able(temp)==1){
                             AG[j]=now;
                             if(j==0)
                                  flag2=1;
                             return 1;
                             }
                             }
                             }    
    return 0;
}
int find(){
     memset(AG,-1,sizeof(AG));
     int sum=0;
     for(int i=0;i<n;i++){
             memset(exist,0,sizeof(exist));
             if(able(i)==1){
                if(i==0)
                   flag1=1;
                  sum++;
  ///               cout<<sum<<" "<<i<<endl;
                  }
                  }
     return sum;
     }

int main(){
    int re;
    while(cin>>n){
          if(n==0)
              break;
          cin>>m>>re;
        //memset(a,0,sizeof(0));
        for(int g=0;g<=n;g++){
                for(int f=0;f<=m;f++){
                        a[g][f]=0;
                        }
                }
        for(int k=0;k<re;k++){
                        int temp1,temp2,temp3;
                        cin>>temp1>>temp2>>temp3;
                        a[temp2][temp3]=1;
                        }
        flag1=0;
        flag2=0;
        int sum=find();
        if(n==1&&m==1){
              cout<<"0"<<endl;    
              }
        else if(n==1){
              sum-=flag1;
              cout<<sum<<endl;
              }
        else if(m==1){
              sum-=flag2;
              cout<<sum<<endl;
              }
        else{
             sum-=flag1+flag2;
              cout<<sum<<endl;
               
                }
                }
    return 0;
}

 

 

 

           poj 1151

              

#include<iostream>
#include<string.h>
using namespace std;
int n,m;
int a[121][121];
int AG[121];
int exist[121];
int able(int now){
    for(int j=1;j<=n;j++){
            if(exist[j]==0&&a[now][j]>0){
                     exist[j]=1;
                     int temp=AG[j];
                     if(temp==-1||able(temp)==1){
                             AG[j]=now;
                             return 1;
                             }
                             }
                             }    
    return 0;
}
int find(){
     memset(AG,-1,sizeof(AG));
     int sum=0;
     for(int i=1;i<=n;i++){
             memset(exist,0,sizeof(exist));
             if(able(i)==1){
                  sum++;
  ///               cout<<sum<<" "<<i<<endl;
                  }
                  }
     return sum;
     }

int main(){
    int a1;
    cin>>a1;
    while(a1--){
    cin>>n;
    int re;
    cin>>re;
        //memset(a,0,sizeof(0));
        for(int g=0;g<=n;g++){
                for(int f=0;f<=n;f++){
                        a[g][f]=0;
                        }
                }
        for(int k=0;k<re;k++){
                        int temp2,temp3;
                        cin>>temp2>>temp3;
                        a[temp2][temp3]=1;
                        }
        int sum=find();
             cout<<n-sum<<endl;
                }
    return 0;
}

 

 

posted on 2012-07-08 21:19  yumao  阅读(909)  评论(0编辑  收藏  举报

导航