Poj 3177

来源:互联网 发布:homebrew mysql 配置 编辑:程序博客网 时间:2024/04/27 21:44

传送门

http://poj.org/problem?id=3177


题目大意

给出一张无向图,问你至少加入多少条边后,图是双联通的。


我的想法

利用Tarjan算法找出所有的桥,删除桥边后原图会变成多个双联通子图,将其缩为一个点,再将桥边补上,这样就成了一棵树,设树中度数为1的点有k 个,那么答案就是k+12


注意事项

1.邻接表中ito[i] 写混(RE 2次)
2.图可能会出现重边,Tarjan算法中u 的父亲不能用节点表示,而要用边的编号表示,不然可能会把一些不是桥的边认为是桥。


代码

#include <cstdio>#include <cstring>#include <algorithm>#define REP(i,_begin,_end) for(int i=(_begin);i!=(_end);++i)template<class T>T min(const T &a,const T &b){return a < b ? a : b;}template<class T>T max(const T &a,const T &b){return a > b ? a : b;}template<class T>bool chkmin(T &a,const T &b){return a > b ? a=b, 1 : 0;}template<class T>bool chkmax(T &a,const T &b){return a < b ? a=b, 1 : 0;}const int SN = 5000 + 100;const int SM = 10000 + 100;int head[SN], nxt[SM<<1], to[SM<<1], bridge[SM][2], tot=1;int dfn[SN], low[SN], belong[SN], d[SN], t;bool del[SM<<1];int n, m, ans, cnt;void Add(int, int);void Tarjan(int, int);void Merge(int, int);void DFS(int, int);int main(){    int x, y, z;    scanf("%d%d",&n,&m);    REP(i, 0, m) scanf("%d%d",&x,&y), Add(x-1, y-1);    Tarjan(0, -1);    REP(i, x=0, n) if(!belong[i]) Merge(i, ++x);    REP(i, 0, cnt) ++d[belong[bridge[i][0]]], ++d[belong[bridge[i][1]]];    REP(i, 0, x) if(d[i+1] == 1) ++ans;    printf("%d\n",(ans+1)>>1);    return 0;}void Add(int u,int v){    nxt[++tot]=head[u];head[u]=tot;to[tot]=v;    nxt[++tot]=head[v];head[v]=tot;to[tot]=u;}void Tarjan(int u,int parent){    dfn[u] = low[u] = ++t;    for(int i=head[u];i;i=nxt[i])        if(i == parent) continue;        else if(!dfn[to[i]]){            Tarjan(to[i], i^1);            if(dfn[u] < low[to[i]])                 del[i]=del[i^1]=true, bridge[cnt][0]=u, bridge[cnt++][1]=to[i];            chkmin(low[u],low[to[i]]);        }        else chkmin(low[u],dfn[to[i]]);}void Merge(int u,int mark){    belong[u] = mark;    for(int i=head[u];i;i=nxt[i])         if(!belong[to[i]] && !del[i])            Merge(to[i], mark);}
0 0
原创粉丝点击