连通图是图论基于联通的一个概念,在ACM中针对图论的考察一部分是也是基于连通图。针对这类问题的解题基本思路就是先求出对应的连通分量(有向图的强连通,无向图的双连通)对图进行简化,然后再结合其他算法计算。
这个题如果能理解题目的话,怎么做就很明显了,能形成一个可以转圈的小群,就相当于一个强连通分量,需要注意的就是这个小群不可以只有一头牛。
#include <set> #include <map> #include <list> #include <stack> #include <queue> #include <ctime> #include <cmath> #include <cstdio> #include <vector> #include <string> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #pragma comment(linker, "/STACK:1024000000,1024000000") #define IT iterator #define PB(x) push_back(x) #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; typedef vector<int> vint; typedef vector<ll> vll; typedef vector<ull> vull; typedef set<int> sint; typedef set<ull> sull; const int maxn = 10000 + 5; vint G[maxn]; int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt; stack<int> S; int a[maxn]; void init(int n) { for (int i = 0; i <= n; i++) G[i].clear(); } void dfs(int u) { pre[u] = lowlink[u] = ++dfs_clock; S.push(u); for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!pre[v]) { dfs(v); lowlink[u] = min(lowlink[u],lowlink[v]); } else if (!sccno[v]) { lowlink[u] = min(lowlink[u],pre[v]); } } if (lowlink[u] == pre[u]) { scc_cnt++; while (1) { int x = S.top(); S.pop(); sccno[x] = scc_cnt; if (x == u) break; } } } void find_scc(int n) { dfs_clock = scc_cnt = 0; CLR(sccno,0); CLR(pre,0); for (int i = 1; i <= n; i++) { if (!pre[i]) dfs(i); } } int cnt[maxn]; int main() { int n,m; //freopen("data.in","r",stdin); while (cin>>n>>m) { init(n); for (int i = 0; i < m; i++) { int a,b; scanf("%d%d",&a,&b); G[a].PB(b); } //for (int i = 0; i <= scc_cnt; i++) new_g[i].clear(); find_scc(n); for (int i = 1; i <= n; i++) { cnt[sccno[i]]++; } int ans = 0; /*cout<<scc_cnt<<endl; for (int i = 1; i <= scc_cnt; i++) { for (int j = 0; j < new_g[i].size(); j++) { cout<<i<<" "<<new_g[i][j].from<<" "<<new_g[i][j].cost<<endl; } }*/ for (int i = 1; i <= scc_cnt; i++) { if (cnt[i] > 1) ans++; } cout<<ans<<endl; } }
2. POJ 1236 Network of Schools
最开始对于图的处理同样也是需要用强连通缩点重新构图,然后针对两个任务,第一个很好理解就是入度为0的强连通个数,第二个问题需要小小想一下,欲将整个图变成一个强连通,那么就相当于把现在的图中入度为0和出度为0的点都消灭掉(加边),那么想到这里答案就出来了,就是max{入度为0的点个数,出度为0的点个数}。
#include <set> #include <map> #include <list> #include <stack> #include <queue> #include <ctime> #include <cmath> #include <cstdio> #include <vector> #include <string> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #pragma comment(linker, "/STACK:1024000000,1024000000") #define IT iterator #define PB(x) push_back(x) #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; typedef vector<int> vint; typedef vector<ll> vll; typedef vector<ull> vull; typedef set<int> sint; typedef set<ull> sull; const int maxn = 10000 + 5; vint G[maxn]; int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt; stack<int> S; int a[maxn]; void init(int n) { for (int i = 0; i <= n; i++) G[i].clear(); } void dfs(int u) { pre[u] = lowlink[u] = ++dfs_clock; S.push(u); for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!pre[v]) { dfs(v); lowlink[u] = min(lowlink[u],lowlink[v]); } else if (!sccno[v]) { lowlink[u] = min(lowlink[u],pre[v]); } } if (lowlink[u] == pre[u]) { scc_cnt++; while (1) { int x = S.top(); S.pop(); sccno[x] = scc_cnt; if (x == u) break; } } } void find_scc(int n) { dfs_clock = scc_cnt = 0; CLR(sccno,0); CLR(pre,0); for (int i = 1; i <= n; i++) { if (!pre[i]) dfs(i); } } int in[maxn]; int out[maxn]; int main() { int n,m; //freopen("data.in","r",stdin); while (cin>>n) { init(n); for (int i = 1; i <= n; i++) { int m; while (scanf("%d",&m),m) { G[i].PB(m); } } //for (int i = 0; i <= scc_cnt; i++) new_g[i].clear(); find_scc(n); CLR(in,0); CLR(out,0); int tmpa = 0,tmpb = 0; for (int i = 1; i <= n; i++) { for (int j = 0; j < G[i].size(); j++) { int v = G[i][j]; if (sccno[i] != sccno[v]) { out[sccno[i]]++; in[sccno[v]]++; } } } for (int i = 1; i <= scc_cnt; i++) { if (!out[i]) tmpb++; if (!in[i]) tmpa++; } cout<<tmpa<<endl<<(scc_cnt == 1 ? 0 : max(tmpb,tmpa))<<endl; } }
题目给出一些仰慕关系,需要求的是被所有牛仰慕的牛的个数。由于图相对比较大,所以先强连通缩点,重新构图之后,这样之后就相对简单了,保证图连通的情况下,我们需要保证出度为0的分量只有一个,如果有多个,那么这几个分量之间必然没有倾慕关系。
#include <set> #include <map> #include <list> #include <stack> #include <queue> #include <ctime> #include <cmath> #include <cstdio> #include <vector> #include <string> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #pragma comment(linker, "/STACK:1024000000,1024000000") #define IT iterator #define PB(x) push_back(x) #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; typedef vector<int> vint; typedef vector<ll> vll; typedef vector<ull> vull; typedef set<int> sint; typedef set<ull> sull; const int maxn = 10000 + 5; vint G[maxn]; int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt; stack<int> S; int a[maxn]; void init(int n) { for (int i = 0; i <= n; i++) G[i].clear(); } void dfs(int u) { pre[u] = lowlink[u] = ++dfs_clock; S.push(u); for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!pre[v]) { dfs(v); lowlink[u] = min(lowlink[u],lowlink[v]); } else if (!sccno[v]) { lowlink[u] = min(lowlink[u],pre[v]); } } if (lowlink[u] == pre[u]) { scc_cnt++; while (1) { int x = S.top(); S.pop(); sccno[x] = scc_cnt; if (x == u) break; } } } void find_scc(int n) { dfs_clock = scc_cnt = 0; CLR(sccno,0); CLR(pre,0); for (int i = 1; i <= n; i++) { if (!pre[i]) dfs(i); } } int out[maxn]; int cnt[maxn]; int main() { int n,m; //freopen("data.in","r",stdin); while (cin>>n>>m) { init(n); for (int i = 0; i < m; i++) { int a,b; scanf("%d%d",&a,&b); G[a].PB(b); } //for (int i = 0; i <= scc_cnt; i++) new_g[i].clear(); find_scc(n); CLR(out,0); CLR(cnt,0); for (int i = 1; i <= n; i++) { cnt[sccno[i]]++; for (int j = 0; j < G[i].size(); j++) { int v = G[i][j]; if (sccno[i] != sccno[v]) { out[sccno[i]]++; } } } bool flag = true; int k = 0; for (int i = 1; i <= scc_cnt; i++) { if (k && !out[i]) { flag = false; break; } if (!out[i]) k = i; } if (flag) cout<<cnt[k]<<endl; else cout<<0<<endl; } }
4. HDU 3861 The King’s Problem
题目读完之后发现应该是最小路径覆盖,但是两个问题:数据范围和图有环。先强连通缩点,重新构图,这样图就变成了DAG,在应用Hungary算法就可以求出答案了。
#include <set> #include <map> #include <list> #include <stack> #include <queue> #include <ctime> #include <cmath> #include <cstdio> #include <vector> #include <string> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #pragma comment(linker, "/STACK:1024000000,1024000000") #define IT iterator #define PB(x) push_back(x) #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; typedef vector<int> vint; typedef vector<ll> vll; typedef vector<ull> vull; typedef set<int> sint; typedef set<ull> sull; const int maxn = 50000 + 5; vint G[maxn]; int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt; stack<int> S; int a[maxn]; void init(int n) { for (int i = 0; i <= n; i++) G[i].clear(); } void dfs(int u) { pre[u] = lowlink[u] = ++dfs_clock; S.push(u); for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!pre[v]) { dfs(v); lowlink[u] = min(lowlink[u],lowlink[v]); } else if (!sccno[v]) { lowlink[u] = min(lowlink[u],pre[v]); } } if (lowlink[u] == pre[u]) { scc_cnt++; while (1) { int x = S.top(); S.pop(); sccno[x] = scc_cnt; if (x == u) break; } } } void find_scc(int n) { dfs_clock = scc_cnt = 0; CLR(sccno,0); CLR(pre,0); for (int i = 1; i <= n; i++) { if (!pre[i]) dfs(i); } } vint new_g[maxn]; int match[maxn]; bool vis[maxn]; bool findpath(int u) { for (int i = 0; i < new_g[u].size(); i++) { int v = new_g[u][i]; if (!vis[v]) { vis[v] = 1; if (match[v] == -1 || findpath(match[v])) { match[v] = u; return true; } } } return false; } int hungary() { int ans = 0; CLR(match,-1); for (int i = 1; i <= scc_cnt; i++) { CLR(vis,0); if (findpath(i)) ans++; } return ans; } int main() { //freopen("data.in","r",stdin); int n,m; int T; cin>>T; while (T--) { cin>>n>>m; init(n); for (int i = 0; i < m; i++) { int a,b; scanf("%d%d",&a,&b); G[a].PB(b); } //for (int i = 0; i <= scc_cnt; i++) new_g[i].clear(); find_scc(n); for (int i = 0; i <= scc_cnt; i++) new_g[i].clear(); for (int i = 1; i <= n; i++) { for (int j = 0; j < G[i].size(); j++) { int v = G[i][j]; if (sccno[i] != sccno[v]) { new_g[sccno[i]].PB(sccno[v]); } } } cout<<scc_cnt - hungary()<<endl; } }
开始还是老套路,先强连通缩点,需要注意的话这个题重新构图需要反向建边,然后对每个入度0的点DFS,求最大值即可。
#include <set> #include <map> #include <list> #include <stack> #include <queue> #include <ctime> #include <cmath> #include <cstdio> #include <vector> #include <string> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #pragma comment(linker, "/STACK:1024000000,1024000000") #define IT iterator #define PB(x) push_back(x) #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; typedef vector<int> vint; typedef vector<ll> vll; typedef vector<ull> vull; typedef set<int> sint; typedef set<ull> sull; const int maxn = 5000 + 5; vint G[maxn]; int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt; stack<int> S; int a[maxn]; void init(int n) { for (int i = 0; i <= n; i++) G[i].clear(); } void dfs(int u) { pre[u] = lowlink[u] = ++dfs_clock; S.push(u); for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!pre[v]) { dfs(v); lowlink[u] = min(lowlink[u],lowlink[v]); } else if (!sccno[v]) { lowlink[u] = min(lowlink[u],pre[v]); } } if (lowlink[u] == pre[u]) { scc_cnt++; while (1) { int x = S.top(); S.pop(); sccno[x] = scc_cnt; if (x == u) break; } } } void find_scc(int n) { dfs_clock = scc_cnt = 0; CLR(sccno,0); CLR(pre,0); for (int i = 0; i < n; i++) { if (!pre[i]) dfs(i); } } vint new_g[maxn]; int rec[maxn]; int out[maxn]; int lab[maxn]; bool vis[maxn]; int cnt; int dp(int u) { vis[u] = true; cnt += lab[u]; for (int i = 0; i < new_g[u].size(); i++) { int v = new_g[u][i]; if (!vis[v]) { dp(v); } } return cnt; } int main() { //freopen("data.in","r",stdin); int n,m; int T; cin>>T; for (int t = 1; t <= T; t++) { cin>>n>>m; init(n); for (int i = 0; i < m; i++) { int a,b; scanf("%d%d",&a,&b); G[a].PB(b); } find_scc(n); CLR(out,0); CLR(lab,0); for (int i = 0; i <= scc_cnt; i++) new_g[i].clear(); for (int i = 0; i < n; i++) { lab[sccno[i]]++; for (int j = 0; j < G[i].size(); j++) { int v = G[i][j]; if (sccno[i] != sccno[v]) { new_g[sccno[v]].PB(sccno[i]); out[sccno[i]]++; } } } CLR(rec,0); int ans = 0; for (int i = 1; i <= scc_cnt; i++) { if (!out[i]) { CLR(vis,0); cnt = 0; rec[i] = dp(i); ans = max(ans,rec[i]); } } /*for (int i = 0; i < n; i++) { cout<<sccno[i]<<" "; } cout<<endl; for (int i = 1; i <= scc_cnt; i++) { cout<<rec[i][0]<<" "<<rec[i][1]<<endl; }*/ printf("Case %d: %d\n",t,ans - 1); bool flag = false; for (int i = 0; i < n; i++) { if (rec[sccno[i]] == ans) { if (flag) printf(" %d",i); else { printf("%d",i); flag = true; } } } printf("\n"); } }
题目很好理解,给出了无向图,断开一条边使得分成的两个部分人数差最小。开始先用tarjan求一下边双联通,确定这个图当中桥有哪些,答案就是这些桥中的一个。然后对于缩完点的图,应该是一棵树,这样的话就可以利用DFS的性质,对这一棵树DFS一遍,就可以求出人数差了。
#include <set> #include <map> #include <list> #include <stack> #include <queue> #include <ctime> #include <cmath> #include <cstdio> #include <vector> #include <string> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #pragma comment(linker, "/STACK:1024000000,1024000000") #define IT iterator #define PB(x) push_back(x) #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; typedef vector<int> vint; typedef vector<ll> vll; typedef vector<ull> vull; typedef set<int> sint; typedef set<ull> sull; const int maxn = 20000 + 5; struct Edge { int to,next; bool cut; bool cong; }; stack<int> S; const int maxm = 40000 + 5; Edge edge[maxm]; int head[maxn],tot; int low[maxn],dfn[maxn],belong[maxn]; int Index; int block; bool Instack[maxn]; int bridge; int peo[maxn]; int tpe[maxn]; void addedge(int u,int v) { edge[tot].to = v; edge[tot].next = head[u]; edge[tot].cut = false; edge[tot].cong = false; head[u] = tot++; } void tarjan(int u,int pre,bool ff) { int v; low[u] = dfn[u] = ++Index; S.push(u); Instack[u] = true; for (int i = head[u]; i != -1; i = edge[i].next) { v = edge[i].to; if (v == pre && (!ff)) continue; if (!dfn[v]) { tarjan(v,u,edge[i].cong); if (low[u] > low[v]) low[u] = low[v]; if (low[v] > dfn[u]) { bridge++; edge[i].cut = true; edge[i^1].cut = true; } } else if (Instack[v] && low[u] > dfn[v]) { low[u] = dfn[v]; } } if (low[u] == dfn[u]) { block++; do { v = S.top(); S.pop(); Instack[v] = false; belong[v] = block; }while (v != u); } } void init() { tot = 0; CLR(head,-1); while (!S.empty()) S.pop(); CLR(tpe,0); } int sum = 0; map<ll,int> edgelab; vint g[maxn]; int minans; int dfs(int u,int pre) { int ans = tpe[u]; for (int i = 0; i < g[u].size(); i++) { if (g[u][i] == pre) continue; ans += dfs(g[u][i],u); } //cout<<ans<<endl; minans = min(minans,abs(sum - 2 * ans)); return ans; } int main() { //freopen("data.in","r",stdin); //freopen("data.out","w",stdout); int n,m; while (cin>>n>>m) { init(); sum = 0; edgelab.clear(); for (int i = 1; i <= n; i++) { scanf("%d",peo+i); sum += peo[i]; } for (int i = 0; i < m; i++) { int a,b; scanf("%d%d",&a,&b); a++;b++; if (a < b) swap(a,b); ll tmp = a * 100000 + b; if (edgelab.find(tmp) != edgelab.end()) { int lab = edgelab[tmp]; edge[lab].cong = true; edge[lab^1].cong = true; } else { edgelab[tmp] = tot; addedge(a,b); addedge(b,a); } } CLR(dfn,0); CLR(Instack,false); Index = block = 0; tarjan(1,0,false); if (block == 1) { cout<<"impossible"<<endl; continue; } for (int i = 0; i <= block; i++) g[i].clear(); minans = 0x3f3f3f3f; for (int i = 1; i <= n; i++) { for (int j = head[i]; j != -1; j = edge[j].next) { if (edge[j].cut) { g[belong[i]].PB(belong[edge[j].to]); } } } //for (int i = 1; i <= n; i++) cout<<belong[i]<<endl; for (int i = 1; i <= n; i++) tpe[belong[i]] += peo[i]; dfs(1,0); cout<<minans<<endl; } }
7. HDU 3849 By Recognizing These Guys, We Find Social Networks Useful
先对顶点进行哈希,然后找到图中的桥,输出出来就行了。需要注意的是,如果图不是连通图的,是不存在桥的概念的,所以缩点之后需要判一下图连通性。
#include <set> #include <map> #include <list> #include <stack> #include <queue> #include <ctime> #include <cmath> #include <cstdio> #include <vector> #include <string> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #pragma comment(linker, "/STACK:1024000000,1024000000") #define IT iterator #define PB(x) push_back(x) #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; typedef vector<int> vint; typedef vector<ll> vll; typedef vector<ull> vull; typedef set<int> sint; typedef set<ull> sull; struct Edge { int to,next; bool cut;//是否是桥 bool cong;//判断重边 }; const int maxn = 20000 + 5; const int maxm = 200000 + 5; Edge edge[maxm]; int head[maxn],tot; int low[maxn],dfn[maxn],Stack[maxn],belong[maxn]; int Index,top; int block; bool Instack[maxn]; int bridge; int n,m; map<string,int> has; map<int,string> fas; void addedge(int u,int v,int tt) { edge[tot].to = v; edge[tot].next = head[u]; edge[tot].cut = false; edge[tot].cong = tt; head[u] = tot++; } void tarjan(int u,int pre) { int v; low[u] = dfn[u] = ++Index; Stack[top++] = u; Instack[u] = true; for (int i = head[u]; i != -1; i = edge[i].next) { v = edge[i].to; if (v == pre) continue; if (!dfn[v]) { tarjan(v,u); if (low[u] > low[v]) low[u] = low[v]; if (low[v] > dfn[u]) { bridge++; edge[i].cut = true; edge[i^1].cut = true; } } else if (Instack[v] && low[u] > dfn[v]) { low[u] = dfn[v]; } } if (low[u] == dfn[u]) { block++; do { v = Stack[--top]; Instack[v] = false; belong[v] = block; } while (v != u); } } int fa[maxn]; void init() { tot = 0; for (int i = 0; i < maxn; i++) fa[i] = i; has.clear(); fas.clear(); CLR(head,-1); } int getf(int x) { if (fa[x] == x) return x; else return fa[x] = getf(fa[x]); } void unit(int x,int y) { x = getf(x); y = getf(y); fa[x] = y; } int main() { int T; cin>>T; while (T--) { scanf("%d%d",&n,&m); init(); int cnt = 1; for (int i = 0; i < m; i++) { char s1[20],s2[20]; scanf("%s%s",s1,s2); string tmpa(s1); string tmpb(s2); int a,b; if (has.count(tmpa)) a = has[tmpa]; else { has[tmpa] = a = cnt++; fas[a] = tmpa; } if (has.count(tmpb)) b = has[tmpb]; else { has[tmpb] = b = cnt++; fas[b] = tmpb; } unit(a,b); addedge(a,b,true); addedge(b,a,false); } CLR(dfn,0); CLR(Instack,false); Index = top = block = 0; tarjan(1,0); int tmp = 0; for (int i = 1; i <= n; i++) { if (fa[i] == i) tmp++; } if (tmp == 1) { printf("%d\n",block-1); for (int i = 1; i <= n; i++) { for (int j = head[i]; j != -1; j = edge[j].next) { if (edge[j].cut && edge[j].cong) { printf("%s %s\n",fas[i].c_str(),fas[edge[j].to].c_str()); } } } } else printf("0\n"); } }
来源:https://www.cnblogs.com/andybear/p/4395630.html