[POJ3352]Road Construction 做题笔记

题目来源:http://poj.org/problem?id=3352
引用一下解题思路:http://blog.csdn.net/geniusluzh/article/details/6619575

这道题的意思是说,给你一个无向图,然后问你至少需要添加几条边,可以使整个图变成边双连通分量,也就是说任意两点至少有两条路可以互相连通。我们这样考虑这个问题,属于同一个边双连通分量的任意点是至少有两条通路是可以互相可达的,因此我们可以将一个边双连通分量缩成一个点。然后考虑不在边双连通分量中的点,通过缩点后形成的是一棵树。对于一个树型的无向图,需要添加(度为1的点的个数+1)/2条边使得图成为双连通的。这样问题就是变成缩点之后,求图中度为1的点的个数了。

图是无向图,跑Tarjan的时候把无向图当成有向图跑就行了(就是不走回头路)
做缩点的题的时候一个比较容易犯的马虎错是把原图的点和新图的点混淆,所以类似degree[bel[ver[i]]]写成degree[ver[i]]的问题一定要注意。
另外写邻接表时有时会把当前结点和子节点混淆(就是把ver[i]写成了now),这也要注意。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2000,M=2000;
int dfn[N],low[N],t=0;
int head[N],nxt[M<<1],ver[M<<1],tot=1;
bool danger[M<<1];
int degree[N],bel[N],scc=0,leaf=0;
int n,m;int col[N];
void add (int u,int v) {
    ver[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}
void Tarjan (int x,int fa) {
    dfn[x]=low[x]=++t;col[x]=-1;
    for (int i=head[x];i;i=nxt[i]) {
        if (ver[i]==fa) continue;
        if (!dfn[ver[i]]) {
            Tarjan(ver[i],x);
            low[x]=min(low[x],low[ver[i]]);
            if (dfn[x]<low[ver[i]])
                danger[i]=danger[i^1]=1;//not node
        }
        else if (col[ver[i]]==-1) low[x]=min(low[x],dfn[ver[i]]);
    }
    col[x]=1;
}
void dfs (int x) {
    bel[x]=scc;
    for (int i=head[x];i;i=nxt[i]) 
        if (!bel[ver[i]]&&!danger[i]) dfs(ver[i]);//bel[ver[i]] instead of bel[x],this is important
}
int solve () {
    for (int i=1;i<=n;i++) {
        if (bel[i]) continue;
        ++scc;dfs(i);
    }
    for (int u=1;u<=n;u++) {
        for (int i=head[u];i;i=nxt[i]) 
            if (bel[u]!=bel[ver[i]]) ++degree[bel[ver[i]]];
    }
    for (int i=1;i<=scc;i++) {
        if (degree[i]==1) ++leaf;
    }
    return ((leaf+1)>>1);
}
int main () {
    int u,v;
    while (scanf("%d %d",&n,&m)!=EOF) {
        memset(head,0,sizeof(head));
        memset(degree,0,sizeof(degree));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(danger,0,sizeof(danger));
        memset(bel,0,sizeof(bel));
        memset(col,0,sizeof(col));
        t=0,tot=1,scc=0,leaf=0;
        for (int i=1;i<=m;i++) {
            scanf("%d%d",&u,&v);
            add(u,v); add(v,u);
        }
        for (int i=1;i<=n;i++) if (!dfn[i]) Tarjan(i,0);
        printf("%d\n",solve());
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值