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的
例如:重边