wannafly 挑戰賽 14 可行性問題 tarjan演算法求連通分量+縮點思想
阿新 • • 發佈:2018-12-13
這道題題意就是給你一個圖然後輸出那些通過該點可到帶圖中任意位置的點。
分析:要找到一些點,這些點的性質是通過這個點可以到達圖中任意位置這就意味著這個點是連線了一個連通分量,那麼在考慮這道題時就要往連通分量這方面考慮了,因為這個聯通分量可能是一個大的連通分量,也可能是幾個連通分量通過幾個邊連在一起,那麼這道題就有了思路,先用tarjan演算法求連通分量並且儲存每個聯通分量的點,然後再按照邊的起點和終點檢視是否在一個聯通分量,如果不在,那麼用這個邊把這兩個聯通分量連在一起,並把其中一個標記,意味著起點的聯通分量裡的任意點可以到達終點聯通分量的點,以此類推,最後沒有被標記的連通分量就是我們要找的點,如果聯通分量有多個點那麼取排序後的第一個;
程式碼:
#include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 20; int head[maxn]; int dfn[maxn]; int low[maxn]; bool vis[maxn]; int sc[maxn]; vector<int>p[maxn]; int tag[maxn]; stack<int>iron; int cnt = 0, ans = 0; int n, m; struct Edge { int from, to, next; }edge[maxn]; void ini() { memset(tag, 0, sizeof(tag)); memset(vis, false, sizeof(vis)); memset(head, -1, sizeof(head)); memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(sc, 0, sizeof(sc)); cnt = 0, ans = 0; } void add(int u, int v) { edge[cnt].from = u; edge[cnt].to = v; edge[cnt].next = head[u]; head[u] = cnt++; } void dfs(int x) { low[x] = dfn[x] = ++cnt; vis[x] = 1; iron.push(x); for (int i = head[x]; ~i; i = edge[i].next) { int j = edge[i].to; if (!dfn[j]) { dfs(j); low[x] = min(low[x], low[j]); } else if (vis[j]) { low[x] = min(low[x], low[j]); } } if (low[x] == dfn[x]) { ++ans; while (!iron.empty()) { int t = iron.top(); vis[t] = 0; p[ans].push_back(t); sc[t] = ans; iron.pop(); if (t == x) break; } } } void tarjan() { for (int i = 1; i <= n; i++) if (!dfn[i]) dfs(i); } int main() { scanf("%d %d", &n, &m); ini(); for (int i = 0; i < m; i++) { int u, v; scanf("%d %d", &u, &v); add(u, v); } cnt = 0, ans = 0; tarjan(); for (int i = 0; i < m; i++) { if (sc[edge[i].from] != sc[edge[i].to]) { tag[sc[edge[i].to]] = 1; } } for (int i = 1; i <= ans; i++) sort(p[i].begin(), p[i].end()); vector<int>daan; for (int i = 1; i <= ans; i++) if ( !tag[ i ] ) daan.push_back(p[i][0]); sort(daan.begin(), daan.end()); printf("%d\n", daan.size()); for (int i = 0; i < daan.size(); i++) printf("%d ", daan[i]); printf("\n"); system("pause"); return 0; }