強連通分量及縮點 演算法解析及例題
演算法講解:https://blog.csdn.net/acmmmm/article/details/16361033#comments
例題:
poj-2186
http://poj.org/problem?id=2186
題意:總共有n頭牛,m個有序對(a, b) 代表牛a崇拜牛b, 崇拜具有傳遞性,求出被其他所有牛崇拜的牛的總數。
題解:構造一個圖,進行求強連通分量及縮點,在強連通分量中的奶牛肯定是被在這個強連通分量裡面奶牛所仰慕的,如果一個圖裡面有幾個強連通分量的話那麼一定至少存在一個出度為0的點,如果不存在那麼剛剛的強連通一定不是最大強連通又想如果有兩個這樣的出度為0的點存在,那麼這個圖中一定沒有滿足題意的奶牛,因為那兩個出度為0的點不會仰慕任何牛所以出度為0的點有且只能有一個!如果有多個那麼滿足題意的牛隻有0個,如果沒有的話那麼整個圖都是滿足題意的牛。
ac code:
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<vector>
using namespace std;
#define N 10005
struct eg {
int to, next;
}ac[5*N];
int n, m, cnt, jar;
int head[N], dfn[N], low[N], instack[N], belong[N];
int nu[N];
vector<int>re[N];
stack<int>s;
void init() {
memset(nu, 0, sizeof(nu));
memset(head, -1, sizeof(head));
memset(dfn, 0, sizeof(dfn));
memset(instack, 0, sizeof(instack));
cnt = 0; jar = 0;
}
void add(int u, int v) {
ac[cnt].to = v;
ac[cnt].next = head[u];
head[u] = cnt++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++cnt;
s.push(u);
instack[u] = 1;
for (int i = head[u]; i != -1; i = ac[i].next) {
int v = ac[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (instack[v])low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
int now;
re[jar].clear();
do {
now = s.top();
s.pop();
instack[now] = 0;
belong[now] = jar;
re[jar].push_back(now);
} while (now != u);
jar++;
}
}
int main() {
int result = 0;
int a, b;
scanf("%d%d", &n, &m);
init();
for (int i = 0; i < m; i++) {
scanf("%d%d", &a, &b);
add(a, b);
}
cnt = 0;
for (int i = 1; i <= n; i++) {
if (dfn[i] == 0)
tarjan(i);
}
for (int i = 1; i <= n; i++) {
for (int j = head[i]; j != -1; j = ac[j].next) {
int v = ac[j].to;
if (belong[i] != belong[v]) {
nu[belong[i]] = 1;
}
}
}
int ans = 0, t;
for (int i = 0; i < jar; i++) {
if (!nu[i]) {
ans++;
t = i;
}
}
if (ans > 1) {
cout << result << endl;
return 0;
}
cout << re[t].size() << endl;
return 0;
}
poj-2553
http://poj.org/problem?id=2553
求從u出發能到v,並且從v出發也能到u的這些點,並且將其輸出;求出一個圖的若干個強連通分量並且如果這個強連通分量中的縮點出度為0的話那麼這個強連通分量裡面的點就都滿足題意。
ac code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
#define N 5005
int n, e, cnt, ans;
int head[N], dfn[N], low[N], belong[N], nu[N], instack[N], vis[N];
vector<int>re[N];
struct eg {
int to, next;
}a[10*N];
stack<int>s;
void init() {
ans = cnt = 0;
memset(head, -1, sizeof(head));
memset(nu, 0, sizeof(nu));
memset(dfn, 0, sizeof(dfn));
memset(instack, 0, sizeof(instack));
memset(vis, 0, sizeof(vis));
}
void add(int u, int v) {
a[cnt].to = v;
a[cnt].next = head[u];
head[u] = cnt++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++cnt;
s.push(u);
instack[u] = 1;
for (int i = head[u]; i != -1; i = a[i].next) {
int v = a[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (instack[v])low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
int now;
re[ans].clear();
do {
now = s.top();
s.pop();
belong[now] = ans;
re[ans].push_back(now);
instack[now] = 0;
} while (now != u);
ans++;
}
}
int main() {
int x, y;
while (~scanf("%d", &n) && n != 0) {
scanf("%d", &e);
init();
for (int i = 0; i < e; i++) {
scanf("%d%d", &x, &y);
add(x, y);
}
cnt = 0;
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
tarjan(i);
}
}
for (int i = 1; i <= n; i++) {
for (int j = head[i]; j != -1; j = a[j].next) {
int v = a[j].to;
if (belong[v] != belong[i]) {
nu[belong[i]] = 1;
}
}
}
for (int i = 0; i < ans; i++) {
if (!nu[i]) {
for (int j = 0; j < re[i].size(); j++) {
vis[re[i][j]] = 1;
}
}
}
int res;
for (int i = 1; i <= n; i++) {
if (vis[i]) {
res = i;
cout << i;
break;
}
}
for (int i = res + 1; i <= n; i++)
if (vis[i])
cout << " " << i;
cout << endl;
}
return 0;
}
poj-2762
http://poj.org/problem?id=2762
題意:一個有向圖中有n個點m條邊,問是否任意兩點u,v之間可以u-v或者v->u。
思路:學過強連通可以知道,在一個強連通中任意兩點都可以滿足題目條件。如果圖中有多個強連通分量,則需要在強連通分量之間建圖,再拓撲排序判斷圖中是否同時存在一個入度為零的兩點,如果存在那麼這兩點肯定不滿足題目條件。
ac code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<vector>
using namespace std;
#define N 1005
int n, m, cnt, ans, t;
int head[N], dfn[N], low[N], belong[N], nu[N], instack[N], vis[N];
vector<int>re[N];
struct eg {
int to, next;
}a[10 * N];
stack<int>s;
void init() {
memset(head, -1, sizeof(head));
memset(dfn, 0, sizeof(dfn));
memset(instack, 0, sizeof(instack));
memset(nu, 0, sizeof(nu));
cnt = ans = 0;
}
void add(int u, int v) {
a[cnt].to = v;
a[cnt].next = head[u];
head[u] = cnt++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++cnt;
s.push(u);
instack[u] = 1;
for (int i = head[u]; i != -1; i = a[i].next) {
int v = a[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (instack[v])low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
int now;
re[ans].clear();
do {
now = s.top();
s.pop();
re[ans].push_back(now);
instack[now] = 0;
belong[now] = ans;
} while (now != u);
ans++;
}
}
void solve() {
set<int>st[N];
vector<int>h;
int flag = 0;
for (int i = 1; i <= n; i++) {
for (int j = head[i]; j != -1; j = a[j].next) {
int v = a[j].to;
if (belong[v] != belong[i]) {
st[belong[i]].insert(belong[v]);
}
}
}
for (int i = 0; i < ans; i++) {
for (set<int>::iterator ite = st[i].begin(); ite != st[i].end(); ite++) {
//cout << i << " " << *ite << endl;
nu[*ite]++;
}
}
for (int i = 0; i < ans; i++) {
if (!nu[i]) {
h.push_back(i);
}
}
while (1) {
if (h.size() == 0)
break;
if (h.size() > 1) {
flag = 1;
cout << "No" << endl;
break;
}
int t = h[0];
h.clear();
for (set<int>::iterator ite = st[t].begin(); ite != st[t].end(); ite++) {
nu[*ite]--;
//cout << *ite << " " << nu[*ite] << endl;
if (nu[*ite] == 0)
h.push_back(*ite);
}
}
if (!flag)
cout << "Yes" << endl;
}
int main() {
int u, v;
cin >> t;
while (t--) {
scanf("%d%d", &n, &m);
init();
for (int i = 0; i < m; i++) {
scanf("%d%d", &u, &v);
add(u, v);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
cnt = 0;
tarjan(i);
}
}
if (ans == 1) {
cout << "Yes" << endl;
}
else {
solve();
}
}
return 0;
}
相關文章
- 強聯通分量及縮點法
- 【模板】tarjan 強連通分量縮點
- 強連通------tarjan演算法詳解及與縮點聯合運用演算法
- 強連通分量(Tarjan演算法)演算法
- Tarjan演算法(強連通分量分解)演算法
- 無向連通圖點雙連通分量
- 強連通分量
- 圖論——強連通分量(Tarjan演算法)圖論演算法
- 【演算法學習】tarjan 強連通、點雙、邊雙及其縮點 重磅來襲!!!!演算法
- kosaraju 和 tarjan演算法詳解(強連通分量)演算法
- Tarjan求強連通分量
- UVA1327 && POJ1904 King's Quest(tarjan+巧妙建圖+強連通分量+縮點)
- Day7 割點、割邊和強連通分量
- POJ 1236 Network of Schools 強連通分量
- 無向連通圖邊雙連通分量
- 尋找圖的強連通分量:tarjan演算法簡單理解演算法
- 有向圖的強連通分量 模版
- 域名解析常見問題盤點及解答
- 20行程式碼實現,使用Tarjan演算法求解強連通分量行程演算法
- Tarjan 求有向圖的強連通分量
- 認知網路知識點及例題總結
- UVA-11504 - Dominos(有向圖的強連通分量)
- 強聯通分量tarjan
- 「學習筆記」雙連通分量、割點與橋筆記
- 多域名解析及延伸知識點
- WebRTC 及點對點網路通訊機制Web
- JS壓縮方法及批量壓縮JS
- Go執行指令碼命令用例及原始碼解析Go指令碼原始碼
- Tarjan演算法_縮點演算法
- Z3求解約束器及例題
- ”回溯演算法“框架及練習題演算法框架
- 圖論系列之「深度優先遍歷及聯通分量」圖論
- 邊分治維護強連通分量(CF1989F,P5163)
- PE檔案格式詳細解析(四)-- 執行時壓縮及UPX壓縮除錯除錯
- Spring原始碼分析(六)SpringAOP例項及標籤的解析Spring原始碼
- Python scrapy增量爬取例項及實現過程解析Python
- Day8 雙連通分量
- sra檔案下載及解析的問題