poj3352 Road Construction & poj3177 Redundant Paths (边双连通分量)题解
题意:有n个点,m条路,问你最少加几条边,让整个图变成边双连通分量。
思路:缩点后变成一颗树,最少加边 = (度为1的点 + 1)/ 2。3177有重边,如果出现重边,用并查集合并两个端点所在的缩点后的点。
代码:
/*3352*/ #include<set> #include<map> #include<stack> #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> typedef long long ll; const int maxn = 5000 + 10; const int seed = 131; const ll MOD = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; struct Edge{ int u, v, next; }edge[maxn << 1]; int index, scc_cnt, tot; //scc_cnt记录SCC int dfn[maxn], low[maxn], sccno[maxn], in[maxn], head[maxn]; stack<int> s; void addEdge(int u, int v){ edge[tot].v = v; edge[tot].u = u; edge[tot].next = head[u]; head[u] = tot++; } void tarjan(int u, int pre){ dfn[u] = low[u] = ++index; s.push(u); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].v; if(!dfn[v]){ tarjan(v, u); low[u] = min(low[u], low[v]); } else if(v != pre){ low[u] = min(low[u], dfn[v]); } } if(dfn[u] == low[u]){ scc_cnt++; int a; while(1){ a=s.top(); s.pop(); sccno[a] = scc_cnt; if(a == u) break; } } } int main(){ int n, m; scanf("%d%d", &n, &m); index = scc_cnt = tot = 0; while(!s.empty()) s.pop(); memset(head, -1, sizeof(head)); memset(dfn, 0, sizeof(dfn)); memset(sccno, 0, sizeof(sccno)); for(int i = 0; i < m; i++){ int u, v; scanf("%d%d", &u, &v); addEdge(u, v); addEdge(v, u); } for(int i = 1; i <= n; i++){ if(!dfn[i]) tarjan(i, 0); } memset(in, 0, sizeof(in)); for(int i = 0; i < tot; i += 2){ int u = edge[i].u, v = edge[i].v; if(sccno[u] != sccno[v]){ in[sccno[u]]++; in[sccno[v]]++; } } int cnt = 0; for(int i = 1; i <= scc_cnt; i++){ if(in[i] == 1) cnt++; } printf("%d\n", (cnt + 1) / 2); return 0; }
/*3177*/ #include<set> #include<map> #include<stack> #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> typedef long long ll; const int maxn = 5000 + 10; const int seed = 131; const ll MOD = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; struct Edge{ int u, v, next; }edge[maxn << 1]; int index, scc_cnt, tot; //scc_cnt记录SCC int dfn[maxn], low[maxn], sccno[maxn], in[maxn], head[maxn]; stack<int> s; map<int, int> mp[maxn]; int Find(int x){ return sccno[x] == x? x : sccno[x] = Find(sccno[x]); } void addEdge(int u, int v){ edge[tot].v = v; edge[tot].u = u; edge[tot].next = head[u]; head[u] = tot++; } void tarjan(int u, int pre){ dfn[u] = low[u] = ++index; s.push(u); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].v; if(!dfn[v]){ tarjan(v, u); low[u] = min(low[u], low[v]); } else if(v != pre){ low[u] = min(low[u], dfn[v]); } } if(dfn[u] == low[u]){ scc_cnt++; int a; while(1){ a=s.top(); s.pop(); sccno[a] = u; if(a == u) break; } } } int main(){ int n, m; scanf("%d%d", &n, &m); index = scc_cnt = tot = 0; while(!s.empty()) s.pop(); memset(head, -1, sizeof(head)); memset(dfn, 0, sizeof(dfn)); memset(sccno, 0, sizeof(sccno)); for(int i = 0; i < m; i++){ int u, v; scanf("%d%d", &u, &v); addEdge(u, v); addEdge(v, u); } for(int i = 1; i <= n; i++){ if(!dfn[i]) tarjan(i, 0); } for(int i = 1; i <= n; i++) mp[i].clear(); memset(in, 0, sizeof(in)); for(int i = 0; i < tot; i += 2){ int u = edge[i].u, v = edge[i].v; if(u > v) swap(u, v); mp[u][v]++; if(mp[u][v] == 2){ int fx = Find(u), fy = Find(v); if(fx != fy){ sccno[fx] = fy; } } } for(int i = 0; i < tot; i += 2){ int u = edge[i].u, v = edge[i].v; int fx = Find(u), fy = Find(v); if(fx != fy){ in[fx]++; in[fy]++; } } int cnt = 0; for(int i = 1; i <= n; i++){ if(sccno[i] == i && in[i] == 1) cnt++; } printf("%d\n", (cnt + 1) / 2); return 0; }