二分图匹配——匈牙利算法:POJ3041解题报告

  昨天晚上终于下定决心一定要学学二分图了。就去看了一会《离散数学》中的“匹配”那一章,了解了相异代表系、最大独立集,最小覆盖之类的概念。

  先说一下题目大意:在一个N*N的矩阵中,有K个点,有一个操作每一次能消除掉一行或者一列上所有的点,问最少要多少次操作才能消除所有的点。

  这题很简单可以看出来是一个二分图的模型。讲行和列视为二分图的两个集合,则一个点(x,y)可以看为在x->y之间连一条边,那么这个问题就是求最小覆盖。

  最小覆盖:V是一个顶点的集合,若图G中每条边都与V中至少一个点相关联,且V是满足上述条件的顶点最小的集合。

  所以直接套用匈牙利算法,代码如下:

  

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>

using namespace std ;

#define N 600

int k1,k2,n,k,ans = 0 ;
int pp[N];
bool a[N][N],p[N];

bool dfs( int t )
{
    for ( int i =1 ; i <= n ; ++i )
    if ( a[t][i] && !p[i] ) 
    {
        p[i] = true ;
        if ( !pp[i] || dfs(pp[i]) )
        {
            pp[i] = t ;
            return true ;
        }
    }
    return false;
}

void init()
{
    scanf("%d%d",&n,&k);
    memset(a,false,sizeof(a));
    memset(pp,0,sizeof(pp));
    while ( k-- )
    {
        scanf("%d%d",&k1,&k2);
        a[k1][k2] = true ;
    }
    return ;
}

void work()
{
    for ( int i = 1 ; i <= n ; ++i )
    {
        memset(p,false,sizeof(p));
        if ( dfs(i) ) ++ans;
    }    
    printf("%d",ans);    
    return ;
}

int main()
{
    init();
    work();
    return 0;
}
POJ3461

  然后从google上找到了别人的blog

http://programmers-in.blogbus.com/logs/47011461.html

算法思想:算法的思路是不停的找增广轨,并增加匹配的个数,增广轨顾名思义是指一条可以使匹配数变多的路径,在匹配问题中,增广轨的表现形式是一条"交错轨",也就是说这条由图的边组成的路径,它的第一条边是目前还没有参与匹配的,第二条边参与了匹配,第三条边没有..最后一条边没有参与匹配,并且始点和终点还没有被选择过.这样交错进行,显然他有奇数条边.那么对于这样一条路径,我们可以将第一条边改为已匹配,第二条边改为未匹配...以此类推.也就是将所有的边进行"反色",容易发现这样修改以后,匹配仍然是合法的,但是匹配数增加了一对.另外,单独的一条连接两个未匹配点的边显然也是交错轨.可以证明,当不能再找到增广轨时,就得到了一个最大匹配.这也就是匈牙利算法的思路.

然后其实这个算法思想我没有理解。。

对于我的代码我是这样理解的:

举个例子,有A、B、C三个学生,然后他们假期有语文、数学、英语、物理等作业要去做,每种作业只能一个人做。在DFS(A)中,发现A会做语文,那么给语文作业打个标签(已经被A选中了)。那么无论如何至少A有作业做了,那么给DFS返回一个非零的值,所以++ans。下来DFS(B),我们如果发现B也会写语文的话,那么{假设B只会写语文作业了,所以A不妨让B写语文作业,A找别的作业做},所以在DFS(B)中,会DFS(pp[语文]),如果A发现自己还会做物理,那么a->物理,b->数学,又增加了一对匹配++ans,反之如果A找不到其他作业会做,那A只能回来写语文,B又继续去看看其他作业他会不会做,不会做的话那么就无法匹配。对于C、D……什么的以此类推,复杂度是O(VE) G=(V,E);

posted @ 2013-05-25 17:00  你的微笑给了我一半  阅读(295)  评论(0编辑  收藏  举报