poj 3177 Redundant Paths 求最少添加几条边成为双联通图: tarjan O(E)

  1 /**
  2 problem: http://poj.org/problem?id=3177
  3 tarjan blog: https://blog.csdn.net/reverie_mjp/article/details/51704523
  4 
  5 v 为下一结点, u为当前结点
  6 如果low[v] > dfn[u] 则 边(u,v)为桥
  7 缩点后剩下的所有边都为桥(缩点后即为树结构)
  8 将叶子结点相连使其成为双联通分量为最优解
  9 所以:
 10 添加(leaf + 1) / 2 条边即可使图成为双联通图
 11 **/
 12 #include<stdio.h>
 13 #include<stack>
 14 #include<algorithm>
 15 using namespace std;
 16 
 17 class Graphics{
 18     const static int MAXN = 5005;
 19     const static int MAXM = 10005 * 2;
 20 private:
 21     struct Edge{
 22         int to, next;
 23         bool bridge;
 24     }edge[MAXM];
 25     struct Point{
 26         int dfn, low, color;
 27     }point[MAXN];
 28     int first[MAXN], sign, sumOfPoint, dfnNum, colorNum;
 29     bool vis[MAXN];
 30     stack<int> stk;
 31     void tarjan(int u, int preEdge = -1){
 32         point[u].low = dfnNum;
 33         point[u].dfn = dfnNum ++;
 34         vis[u] = true;
 35         stk.push(u);
 36         for(int i = first[u]; i != -1; i = edge[i].next){
 37             int to = edge[i].to;
 38             if((i^1) == preEdge) continue; /// 由于是双向边,防止由该边跑回原来的点
 39             if(!point[to].dfn){
 40                 tarjan(to, i);
 41                 point[u].low = min(point[u].low, point[to].low);
 42                 if(point[to].low > point[u].dfn){
 43                     edge[i].bridge = true;
 44                     edge[i^1].bridge = true;
 45                 }
 46             }else if(vis[to]){
 47                 point[u].low = min(point[to].dfn, point[u].low);
 48             }
 49         }
 50         if(point[u].dfn == point[u].low){
 51             vis[u] = false;
 52             point[u].color = ++ colorNum;
 53             while(stk.top() != u){
 54                 point[stk.top()].color = colorNum;
 55                 vis[stk.top()] = false;
 56                 stk.pop();
 57             }
 58             stk.pop();
 59         }
 60     }
 61 public:
 62     void init(int n){
 63         sumOfPoint = n;
 64         for(int i = 1; i <= n; i ++){
 65             first[i] = -1;
 66             vis[i] = 0;
 67         }
 68         sign = colorNum = 0;
 69         dfnNum = 1;
 70     }
 71     void addEdgeOneWay(int u, int v){
 72         edge[sign].to = v;
 73         edge[sign].next = first[u];
 74         edge[sign].bridge = false;
 75         first[u] = sign ++;
 76     }
 77     void addEdgeTwoWay(int u, int v){
 78         addEdgeOneWay(u, v);
 79         addEdgeOneWay(v, u);
 80     }
 81     void tarjanAllPoint(){
 82         for(int i = 1; i <= sumOfPoint; i ++){
 83             if(!point[i].dfn)
 84                 tarjan(i);
 85         }
 86     }
 87     int getAns(){
 88         int *degree = new int[sumOfPoint+1];
 89         int ans = 0;
 90         for(int i = 1; i <= sumOfPoint; i ++){
 91             degree[i] = 0;
 92         }
 93         tarjanAllPoint();
 94         for(int i = 1; i <= sumOfPoint; i ++){
 95             for(int j = first[i]; j != -1; j = edge[j].next){
 96                 int to = edge[j].to;
 97                 if(edge[j].bridge){
 98                     degree[point[to].color] ++;
 99                 }
100             }
101         }
102         for(int i = 1; i <= sumOfPoint; i ++){
103             if(degree[i] == 1){
104                 ans ++;
105             }
106         }
107         delete []degree; return (ans + 1) / 2;
108     }
109 }graph;
110 
111 int main(){
112     int f, r;
113     scanf("%d%d", &f, &r);
114     graph.init(f);
115     while(r --){
116         int a, b;
117         scanf("%d%d", &a, &b);
118         graph.addEdgeTwoWay(a, b);
119     }
120     printf("%d\n", graph.getAns());
121     return 0;
122 }

ps:

防止由该边跑回原来的点不能判断(点)而要判断(边)即

这么写是有bug的
例如:重边

posted @ 2019-03-13 22:22  DarkScoCu  阅读(179)  评论(0编辑  收藏  举报