POJ 3352 边的双连通分量

被lrj的书坑害了,上面根本没有提到边双连通分量!!居然对于双连通分量的定义就是没有割点的连通分支!!我草!!!这是点双连通!!!还缩点?缩你妹!!!点双连通分量之间的点集是会相交的,根本没法缩点!!只有边双连通分量才能缩点啊!!!每个桥就是两个边双连通分量之间的边!!!有木有!!

晚上躺在床上直接想到了一点钟,终于把这个算法的每个细节都想明白了,图论的最短路和网络流确实是最水的,网络流最多就是建图难一点,至于算法则是很朴素的,而这个边双连通分量的tarjan算法的细节之多是无法想象的!虽然只有几行但是没有研究过这个算法的人根本不知道在这几行的背后你需要判断和证明多少东西!!!

再说说那个点双连通分量,通过割点把图分成一个个边的不相交的集合,后面想了想这种双连通分量如果要缩块的话只有对边进行收缩了,因为边和边之间是不相交的,可以把每条边都标上一个序号,那么这个序号是唯一的,就可把每个有相同序号的边看做一个集合,对边进行收缩,每个边的集合都有属于这个集合的点集,两个不相同的集合会有点集相交,相交的点最多有一个就是他们的割点,对于每两个有点相交的集合可以连一条边,那么割点在集合与集合之间的作用就相当于边~

感谢下byvoid牛,http://www.byvoid.com/blog/biconnect/#respond,神一般的日志,受教了!!!

#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
#include<string>
#include<iomanip>
#include<cassert>
#include<algorithm>
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 1011;

vector<int>g[maxn];
vector<int>v[maxn];
vector<int>s;
int dfn[maxn];
int low[maxn];
bool ins[maxn];
bool hash[maxn][maxn];
int f[maxn];
int t[maxn];
int df,nf,n,m;

void tarjan(int now)
{
    low[now] = dfn[now] = df++;
    s.push_back(now);
    ins[now]=true;
    int to;
    for(int i=0;i<g[now].size();i++)
    {
        to = g[now][i];
        if(!dfn[to])
        {
            t[to]=now;
            tarjan(to);
            low[now] = min(low[to],low[now]);
        }
        else if(to!=t[now])
        {
            low[now]=min(dfn[to],low[now]);
        }
    }
    if(dfn[now] == low[now])
    {
        while(s.back() != now)
        {
            to = s.back();
            f[to] = nf;
            s.pop_back();
            ins[to]=false;
        }
        to = s.back();
        s.pop_back();
        ins[to]=false;
        f[to] = nf++;
    }
    return ;
}


int start()
{
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(ins,false,sizeof(ins));
    s.clear();
    memset(f,-1,sizeof(f));
    memset(t,-1,sizeof(t));
    df = nf = 1;
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
        {
            tarjan(i);
        }
    }
    nf --;
    memset(hash,false,sizeof(hash));
    for(int i=0;i<=nf;i++)
    {
        v[i].clear();
    }
    int from,to;
    for(int i=1;i<=n;i++)
    {
        from = f[i];
        for(int j=0;j<g[i].size();j++)
        {
            to = f[g[i][j]];
            if(from != to && !hash[from][to])
            {
                hash[from][to]=true;
                hash[to][from]=true;
                v[from].push_back(to);
                v[to].push_back(from);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=nf;i++)
    {
        if(v[i].size()==1)
        {
            ans++;
        }
    }
    return (ans+1)/2;
}

int main()
{
    while(cin>>n>>m)
    {
        int x,y;
        for(int i=0;i<=n;i++)
        {
            g[i].clear();
        }
        memset(hash,false,sizeof(hash));
        for(int i=1;i<=m;i++)
        {
            cin>>x>>y;
            if(!hash[x][y])
            {
                hash[x][y]=true;
                hash[y][x]=true;
                g[x].push_back(y);
                g[y].push_back(x);
            }
        }
        cout<<start()<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值