二分图 zoj 1137 poj 1325 poj1422 hdu2063 hdu1498

来源:互联网 发布:交大医学院网络英语 编辑:程序博客网 时间:2024/04/29 22:26

下面是对匈牙利算法的初步理解:

匈牙利算法是解决寻找二分图最大匹配的。


(一)预备知识
    什么是二分图:二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。

    什么是匹配:把上图想象成3男4女搞对象(无同性恋),连线代表彼此有好感,但最终只能1夫1妻,最终的配对结果连线就是一个匹配。匹配可以是空。
    什么是最大匹配:在有好感的基础上,能够最多发展几对。

    现在要用匈牙利算法找出最多能发展几对。
[color=green][size=medium]
匈牙利算法是解决寻找二分图最大匹配的。

(二)匈牙利算法正文:
    匈牙利算法中有个非常非常最重要的概念叫做-交错路径,或者叫交错树,指的同一个东西。交错路径是这样的。现在有一个匹配如图黑线所示

    则其交错路径为:

    交错路径是对于一个匹配而言的,交错路径的起点和终点必须不是匹配中的点,而且匹配中的边在交错路径中交替出现。
    先给一个例子
    1、起始没有匹配

    2、选中第一个x点找第一跟连线

    3、选中第二个点找第二跟连线

    4、发现x3的第一条边x3y1已经被人占了,找出x3出发的的交错路径x3-y1-x1-y4,把交错路中已在匹配上的边x1y1从匹配中去掉,剩余的边x3y1 x1y4加到匹配中去

    5、同理加入x4,x5。
    
    匈牙利算法可以深度有限或者广度优先,刚才的示例是深度优先,即x3找y1,y1已经有匹配,则找交错路。若是广度优先,应为:x3找y1,y1有匹配,x3找y2。

zoj1137

求二分图的最大独立点集 , 用匈牙利算法先求出最大匹配数 , 然后用总数减去最大匹配数就是题目要求的答案

Description

the second year of the university somebody started a study on the romantic relations between the students. The relation “romantically involved” is defined between one girl and one boy. For the study reasons it is necessary to find out the maximum set satisfying the condition: there are no two students in the set who have been “romantically involved”. The result of the program is the number of students in such a set. 

The input contains several data sets in text format. Each data set represents one set of subjects of the study, with the following description: 

the number of students 
the description of each student, in the following format 
student_identifier:(number_of_romantic_relations) student_identifier1 student_identifier2 student_identifier3 ... 
or 
student_identifier:(0) 

The student_identifier is an integer number between 0 and n-1, for n subjects. 
For each given data set, the program should write to standard output a line containing the result. 
 

Sample Input

70: (3) 4 5 61: (2) 4 62: (0)3: (0)4: (2) 0 15: (1) 06: (2) 0 130: (2) 1 21: (1) 02: (1) 0
 

Sample Output

52
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;int map[1100][1100],vis[1100],pre[1100];int t;int dfs(int x){    for(int i=0; i<t; i++)    {        if(!vis[i]&&map[x][i])        {            vis[i]=1;            if(pre[i]==-1||dfs(pre[i]))            {                pre[i]=x;                return 1;            }        }    }    return 0;}int main(){    int a,b,c;    while(~scanf("%d",&t))    {        memset(map,0,sizeof(map));        memset(pre,-1,sizeof(pre));        for(int i=0; i<t; i++)        {            scanf("%d",&a);            getchar();            getchar();            getchar();            scanf("%d",&b);            getchar();            while(b--)            {                scanf("%d",&c);                map[a][c]=1;                map[c][a]=1;            }        }        int sum=0;        for(int i=0; i<t; i++)        {            memset(vis,0,sizeof(vis));              dfs(i);        }       for(int i=0;i<t;i++)       {           if(pre[i]!=-1)            sum++;       }       printf("%d\n",t-sum/2);    }}
</pre><pre code_snippet_id="451495" snippet_file_name="blog_20140815_1_8352899" name="code" class="cpp">
</pre><pre code_snippet_id="451495" snippet_file_name="blog_20140815_1_8352899" name="code" class="cpp">
</pre></div>

poj 1325
题意:有K份工作,A,B两台机器都有一个模式能完成,A,B最初都在0模式。问最少要换多少次模式才能完成所有工作。

思路:刚开始审题,错以为工作是要按顺序完成,想到要去找答案。才知道不需要按顺序完成,这样就是简单的求最小点覆盖。  A,B两台机器的所有模式(除0模式)分别成为二分图的二边,能完成同一份工作的有边连接。如果一个工作有一台的0模式能完成,不需要加到二分图中。用匈牙利算法求出最大匹配,由最小点覆盖=最大匹配。所以,你懂得。。。。。。。

Description

As we all know, machine scheduling is a very classical problem in computer science and has been studied for a very long history. Scheduling problems differ widely in the nature of the constraints that must be satisfied and the type of schedule desired. Here we consider a 2-machine scheduling problem. 

There are two machines A and B. Machine A has n kinds of working modes, which is called mode_0, mode_1, …, mode_n-1, likewise machine B has m kinds of working modes, mode_0, mode_1, … , mode_m-1. At the beginning they are both work at mode_0. 

For k jobs given, each of them can be processed in either one of the two machines in particular mode. For example, job 0 can either be processed in machine A at mode_3 or in machine B at mode_4, job 1 can either be processed in machine A at mode_2 or in machine B at mode_4, and so on. Thus, for job i, the constraint can be represent as a triple (i, x, y), which means it can be processed either in machine A at mode_x, or in machine B at mode_y. 

Obviously, to accomplish all the jobs, we need to change the machine's working mode from time to time, but unfortunately, the machine's working mode can only be changed by restarting it manually. By changing the sequence of the jobs and assigning each job to a suitable machine, please write a program to minimize the times of restarting machines. 
 

Input

The input file for this program consists of several configurations. The first line of one configuration contains three positive integers: n, m (n, m < 100) and k (k < 1000). The following k lines give the constrains of the k jobs, each line is a triple: i, x, y. 

The input will be terminated by a line containing a single zero. 
 

Output

The output should be one integer per line, which means the minimal times of restarting machine. 
 

Sample Input

5 5 100 1 11 1 22 1 33 1 44 2 15 2 26 2 37 2 48 3 39 4 30
 

Sample Output

3
 
</pre><pre code_snippet_id="451495" snippet_file_name="blog_20140815_4_3555324" name="code" class="cpp" style="font-family: Verdana, Arial, sans-serif; color: rgb(51, 51, 51);">
</pre><pre code_snippet_id="451495" snippet_file_name="blog_20140815_4_3555324" name="code" class="cpp" style="font-family: Verdana, Arial, sans-serif; color: rgb(51, 51, 51);">
</pre><pre code_snippet_id="451495" snippet_file_name="blog_20140815_4_3555324" name="code" class="cpp" style="font-family: Verdana, Arial, sans-serif; color: rgb(51, 51, 51);">#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;int map[1100][1100];int vis[550],pre[550];int k,n,m;int dfs(int x){    for(int i=0; i<m; i++)    {        if(!vis[i]&&map[x][i])        {            vis[i]=1;            if(pre[i]==-1||dfs(pre[i]))            {                pre[i]=x;                return 1;            }        }    }    return 0;}int main(){    int a,b,c;    while(scanf("%d",&k)&&k)    {        memset(map,0,sizeof(map));        memset(pre,-1,sizeof(pre));        scanf("%d %d",&m,&n);        while(n--)        {            scanf("%d %d %d",&a,&b,&c);            if(c>0&&b>0)            map[b][c]=1;        }        int sum=0;        for(int i=0; i<k; i++)        {            memset(vis,0,sizeof(vis));            sum+=dfs(i);        }        printf("%d\n",sum);    }}
poj 1422 
这个问题是二分图的最小路径覆盖问题,路径覆盖的定义是:在有向图中找一些路径,使之覆盖了图中的所有顶点,就是任意一个顶点都跟那些路径中的某一条相关联,且任何一个顶点有且只有一条路径与之关联,一个单独的顶点是一条路径……最小路径覆盖就是最少的路径覆盖数.

Description

Consider a town where all the streets are one-way and each street leads from one intersection to another. It is also known that starting from an intersection and walking through town's streets you can never reach the same intersection i.e. the town's streets form no cycles.

With these assumptions your task is to write a program that finds the minimum number of paratroopers that can descend on the town and visit all the intersections of this town in such a way that more than one paratrooper visits no intersection. Each paratrooper lands at an intersection and can visit other intersections following the town streets. There are no restrictions about the starting intersection for each paratrooper. 
 

Input

Your program should read sets of data. The first line of the input file contains the number of the data sets. Each data set specifies the structure of a town and has the format: 

no_of_intersections 
no_of_streets 
S1 E1 
S2 E2 
...... 
Sno_of_streets Eno_of_streets 

The first line of each data set contains a positive integer no_of_intersections (greater than 0 and less or equal to 120), which is the number of intersections in the town. The second line contains a positive integer no_of_streets, which is the number of streets in the town. The next no_of_streets lines, one for each street in the town, are randomly ordered and represent the town's streets. The line corresponding to street k (k <= no_of_streets) consists of two positive integers, separated by one blank: Sk (1 <= Sk <= no_of_intersections) - the number of the intersection that is the start of the street, and Ek (1 <= Ek <= no_of_intersections) - the number of the intersection that is the end of the street. Intersections are represented by integers from 1 to no_of_intersections. 

There are no blank lines between consecutive sets of data. Input data are correct. 
 

Output

The result of the program is on standard output. For each input data set the program prints on a single line, starting from the beginning of the line, one integer: the minimum number of paratroopers required to visit all the intersections in the town. 
 

Sample Input

2433 41 32 3331 31 22 3
 

Sample Output

21
 
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;int map[1100][1100],vis[1100],pre[1100];int t,a;int dfs(int x){    for(int i=1; i<=a; i++)    {        if(!vis[i]&&map[x][i])        {            vis[i]=1;            if(pre[i]==-1||dfs(pre[i]))            {                pre[i]=x;                return 1;            }        }    }    return 0;}int main(){    int b,c,d;    scanf("%d",&t);    while(t--)    {        memset(map,0,sizeof(map));        memset(pre,-1,sizeof(pre));        scanf("%d %d",&a,&b);        while(b--)        {            scanf("%d %d",&c,&d);            map[c][d]=1;        }        int sum=0;        for(int i=1; i<=a; i++)        {            memset(vis,0,sizeof(vis));            sum+= dfs(i);        }        printf("%d\n",a-sum);    }}
hdu 2063

Description

RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了。可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个个男生做partner和她同坐。但是,每个女孩都有各自的想法,举个例子把,Rabbit只愿意和XHD或PQK做partner,Grass只愿意和linle或LL做partner,PrincessSnow愿意和水域浪子或伪酷儿做partner。考虑到经费问题,boss刘决定只让找到partner的人去坐过山车,其他的人,嘿嘿,就站在下面看着吧。聪明的Acmer,你可以帮忙算算最多有多少对组合可以坐上过山车吗?
 

Input

输入数据的第一行是三个整数K , M , N,分别表示可能的组合数目,女生的人数,男生的人数。0<K<=1000 
1<=N 和M<=500.接下来的K行,每行有两个数,分别表示女生Ai愿意和男生Bj做partner。最后一个0结束输入。
 

Output

对于每组数据,输出一个整数,表示可以坐上过山车的最多组合数。
 

Sample Input

6 3 31 11 21 32 12 33 10
 

Sample Output

3
 
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;int map[1100][1100];int vis[550],pre[550];int k,n,m;int dfs(int x){    for(int i=1; i<=n; i++)    {        if(!vis[i]&&map[x][i])        {            vis[i]=1;            if(pre[i]==-1||dfs(pre[i]))            {                pre[i]=x;                return 1;            }        }    }    return 0;}int main(){    int a,b;    while(scanf("%d",&k)&&k)    {        memset(map,0,sizeof(map));        memset(pre,-1,sizeof(pre));        scanf("%d %d",&m,&n);        while(k--)        {            scanf("%d %d",&a,&b);            map[a][b]=1;        }        int sum=0;        for(int i=1; i<=m; i++)        {            memset(vis,0,sizeof(vis));            sum+=dfs(i);        }        printf("%d\n",sum);    }}

以上都是简单的模版题目


hdu1498

我们先考虑矩阵内一种颜色的情况,如果要我们用最小的次数打掉所以的红色的气球,因为我们每次能打掉同一行或同一列的, 由于每次撞气球都是用行或者列的办法,那么问题就转化成了用最少的行或列来撞破红色的气球,于是想到了二分图的最小顶点覆盖………

将行作为左边的点,列作为右边的点,原图中的每个点相当于二分图中的每一行边,
而用最小的行或者列去打掉气球,就相当于用二分图中最小的顶点去覆盖掉所有的边

那么50种颜色??分别枚举单种颜色,再分别判断就可以了…
枚举所有的颜色,每次都比较最大二分匹配的值与所给值的大小,若大于给定值,那么就表示不能完成。

Description

On Octorber 21st, HDU 50-year-celebration, 50-color balloons floating around the campus, it's so nice, isn't it? To celebrate this meaningful day, the ACM team of HDU hold some fuuny games. Especially, there will be a game named "crashing color balloons". 

There will be a n*n matrix board on the ground, and each grid will have a color balloon in it.And the color of the ballon will be in the range of [1, 50].After the referee shouts "go!",you can begin to crash the balloons.Every time you can only choose one kind of balloon to crash, we define that the two balloons with the same color belong to the same kind.What's more, each time you can only choose a single row or column of balloon, and crash the balloons that with the color you had chosen. Of course, a lot of students are waiting to play this game, so we just give every student k times to crash the balloons. 

Here comes the problem: which kind of balloon is impossible to be all crashed by a student in k times. 


 

Input

There will be multiple input cases.Each test case begins with two integers n, k. n is the number of rows and columns of the balloons (1 <= n <= 100), and k is the times that ginving to each student(0 < k <= n).Follow a matrix A of n*n, where Aij denote the color of the ballon in the i row, j column.Input ends with n = k = 0. 
 

Output

For each test case, print in ascending order all the colors of which are impossible to be crashed by a student in k times. If there is no choice, print "-1". 
 

Sample Input

1 112 11 11 22 11 22 25 41 2 3 4 52 3 4 5 13 4 5 1 24 5 1 2 35 1 2 3 43 350 50 5050 50 5050 50 500 0
 

Sample Output

-1121 2 3 4 5-1
 

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;int n,t;int map[1100][1100],a[1100][100];int vis[1100],pre[1100],c[1100],b[1100];int dfs(int x){    for(int i=1; i<=n; i++)    {        if(!vis[i]&&a[x][i])        {            vis[i]=1;            if(pre[i]==-1||dfs(pre[i]))            {                pre[i]=x;                return 1;            }        }    }    return 0;}int main(){    int sum;    while(scanf("%d %d",&n,&t),n||t)    {        memset(map,0,sizeof(map));        memset(c,0,sizeof(c));        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)            {                scanf("%d",&map[i][j]);                c[map[i][j]]=1;            }        for(int i=1; i<=50; i++)        {            if(c[i])            {                memset(a,0,sizeof(a));                memset(pre,-1,sizeof(pre));                for(int j=1; j<=n; j++)                    for(int k=1; k<=n; k++)                    {                        if(map[j][k]==i)                            a[j][k]=1;                    }            }            sum=0;            for(int j=1; j<=n; j++)            {                memset(vis,0,sizeof(vis));                if(dfs(j))                    sum++;            }            if(sum<=t)                c[i]=0;        }        int num=0;        memset(b,0,sizeof(b));        for(int k=1;k<=50;k++)           {               if(c[k])                b[num++]=k;           }        if(num==0)            printf("-1\n");        else        {            for(int i=0;i<num-1;i++)                printf("%d ",b[i]);            printf("%d\n",b[num-1]);        }    }    return 0;}
我们先考虑矩阵内一种颜色的情况,如果要我们用最小的次数打掉所以的红色的气球,因为我们每次能打掉同一行或同一列的, 由于每次撞气球都是用行或者列的办法,那么问题就转化成了用最少的行或列来撞破红色的气球,于是想到了二分图的最小顶点覆盖……… 将行作为左边的点,列作为右边的点,原图中的每个点相当于二分图中的每一行边,而用最小的行或者列去打掉气球,就相当于用二分图中最小的顶点去覆盖掉所有的边 那么50种颜色??分别枚举单种颜色,再分别判断就可以了…枚举所有的颜色,每次都比较最大二分匹配的值与所给值的大小,若大于给定值,那么就表示不能完成。

0 1