poj 1904 Tarjan

囧囧的。。。本来想了解下二分图匹配的,所以去搜HDU上的题,看到一道说是二分匹配的题(hdu 4685),然后看了之后说和POJ 1904很像,由于代码看不懂所以就去翻了POJ1904,然后发现是用强连通做的。。于是我又去看了Tarjan。。。然后当我回过头来。。。二分匹配呢。。。

*******************************************

其实也是初初涉猎图论,所以多了解一点自己还是很开心的。Tarjan算法本身其实挺好理解的,应该是用来找连通分量的。但是我觉得这道题难点应该是在如何构图,为什么这么建图。。撸了一下午,一知半解吧。看了题目很不解,为什么需要再列wizard的列表,然后用公主指向王子。。。 想了一下发现,如果不回指的话,每个公主就会形成独立块,根本不能联通。(然后为什么是这样指呢我又不明白了,看HDU4685就没有这个条件。。。)当我们分出每个联通块后,在王子的块内的公主就都可以选啦,因为这个时候绝对会有不冲突的匹配。感觉Tarjan用的很巧!

然后代码和网上的是一样的。。。

#include <stdio.h>
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
#define maxn 4010
stack<int>stap;    <span style="white-space:pre">		</span>//STL自己还不是很熟,话说stack和vector有什么区别?
vector<int>ans;
int instack[maxn],low[maxn],dfn[maxn],map[maxn][maxn],belong[maxn]; //开两倍
int countt,cnt;
int n;
void tarjan(int x)
{
    int i;
    stap.push(x);
    dfn[x]=low[x]=++cnt;
    instack[x]=1;
    for(i=1;i<=n*2;i++)
    {
        if(!map[x][i]) continue;
        if(!dfn[i])
        {
            tarjan(i);
            low[x]=min(low[x],low[i]);    //注意这里的比较是和什么比,这个是在碰到自环的点或者是找到环返回后更新的
        }
        else if(instack[i]) low[x]=min(low[x],dfn[i]);   //如果碰到一个在队列内的(就是找到环了,i就是根节点,所以以它的dfn为low)
    }
    
    if(low[x]==dfn[x])
    {
        countt++;
        while(1)
        {
            int tmp=stap.top();
            stap.pop();
            belong[tmp]=countt;
            instack[tmp]=0;
            if(tmp==x) break;
        }
    }
}
void init()
{
    countt=cnt=0;
    memset(map,0,sizeof(map));
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    memset(instack,0,sizeof(instack));
    memset(belong,0,sizeof(belong));
}
void work()
{
    
    int i;
    for(i=1;i<=n+n;i++)
        if(!dfn[i]) tarjan(i);
}
int main()
{
    while(~scanf("%d",&n))
    {
        int i,j,k;
        init();
        for(i=1;i<=n;i++)
        {
            scanf("%d",&j);
            while(j--)
            {
                scanf("%d",&k);
                map[i][k+n]=1;
            }
        }
        for(i=1;i<=n;i++)
        {
            scanf("%d",&j);
            map[j+n][i]=1;
        }
        
        work();
        
        for(i=1;i<=n;i++)
        {
            ans.clear();
            for(j=n+1;j<=n*2;j++)
            {
                if(map[i][j]&&belong[i]==belong[j])
                    ans.push_back(j-n);
            }
            printf("%d",ans.size());
            for(j=0;j<ans.size();j++) printf(" %d“,ans[j]);
            printf("\n");
        }
        
    }
    return 0;
}
**************************************

在找Tarjan的时候,碰到一个巨巨byvoid,清华的大神,觉得非常膜拜。

*********************************************

关于匈牙利算法的问题解释

定理:

如果从一个点A出发,没有找到增广路径,那么无论再从别的点出发找到多少增广路径来改变现在的匹配,从A出发都永远找不到增广路径。要用文字来证明这个定理很繁,话很难说,要么我还得多画一张图,我在此就省了。其实你自己画几个图,试图举两个反例,这个定理不难想通的。(给个提示。如果你试图举个反例来说明在找到了别的增广路径并改变了现有的匹配后,从A出发就能找到增广路径。那么,在这种情况下,肯定在找到别的增广路径之前,就能从A出发找到增广路径。这就与假设矛盾了。)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值