参考博客:浅析强连通分量(Tarjan和kosaraju)
Tarjan·关于图的连通性 & 连通分量
题目:1332 上白泽慧音
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5010;
const int N = 50010;
stack<int>s;
int n, m, idx = 0, k = 1, Bcnt = 0;
int head[N];
int ins[maxn];
int dfn[maxn], low[maxn];
int belong[maxn];
set<int>g[maxn];
set<int>::iterator it;
int ans = 0;
struct edge
{
int v, next;
} e[N];
void add(int u, int v)
{
e[k].v = v;
e[k].next = head[u];
head[u] = k++;
}
void tarjan(int u)
{
int v;
dfn[u] = low[u] = ++idx;
ins[u] = 1;
s.push(u);
for(int i = head[u]; i != -1; i = e[i].next)
{
v = e[i].v;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(ins[v])low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
Bcnt++;
do
{
v = s.top();
s.pop();
ins[v] = 0;
g[Bcnt].insert(v);
}
while(u != v);
int f = g[Bcnt].size();
ans = max(f, ans);
}
}
void init()
{
idx = 0, k = 1, Bcnt = 0, ans = 0;
memset(ins, 0, sizeof(ins));
memset(dfn, 0, sizeof(dfn));
memset(low, 0,sizeof(low));
memset(head, -1, sizeof(head));
}
int main()
{
int T, p, u, v, choose;
init();
scanf("%d %d", &n, &p);
for(int i = 1; i <= p; i++)
{
scanf("%d %d %d", &u, &v, &choose);
add(u, v);
if(choose == 2)add(v, u);
}
for(int i= 1; i <= n; i++)if(!dfn[i])tarjan(i);
int mi = INT_MAX, pos = 0;
cout<<ans<<endl;
for(int i = 1; i <= Bcnt; i++)
{
if(g[i].size() == ans)
{
int first_num = *(g[i].begin());
if(mi > first_num)
{
pos = i;
mi = first_num;
}
}
}
int t = 0;
for(it = g[pos].begin(); it != g[pos].end(); it++)
{
if(t != 0)cout<<" ";
printf("%d", *it);
t++;
}
cout<<endl;
return 0;
}
题目:Popular Cows POJ - 2186
题意:就是有N头牛,给你M中关系,问你有多少头牛受欢迎,当一个牛被除了自己本身的所有牛喜欢那么这头牛就是最受欢迎的牛。
题解:本题采用Tarjan+缩点,把强连通分量看成一个点,统计出度为0的点有多少个,如果出度为0的点为一个的话,那么则这个点(可能是强连通分量缩的点)就是最受欢的,输出这个点包含多少头牛。如果出度为0的点有两个以上,那么就不存在最受欢迎的牛。
代码:
#include<stdio.h>
#include<iostream>
#include<stack>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 10010;
const int N = 50010;
stack<int>s;
int n, m, idx = 0, k = 1, Bcnt = 0;
int head[N];
int ins[maxn];
int dfn[maxn], low[maxn];
int belong[maxn];
int num[maxn];
int degree[maxn];
struct edge
{
int v, next;
} e[N];
void add(int u, int v)
{
e[k].v = v;
e[k].next = head[u];
head[u] = k++;
}
void tarjan(int u)
{
int v;
dfn[u] = low[u] = ++idx;
ins[u] = 1;
s.push(u);
for(int i = head[u]; i != -1; i = e[i].next)
{
v = e[i].v;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(ins[v])low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
Bcnt++;
do
{
v = s.top();
s.pop();
ins[v] = 0;
belong[v] = Bcnt;
num[Bcnt]++;
}
while(u != v);
}
}
void init()
{
idx = 0, k = 1, Bcnt = 0;
memset(ins, 0, sizeof(ins));
memset(dfn, 0, sizeof(dfn));
memset(low, 0,sizeof(low));
memset(head, -1, sizeof(head));
memset(num, 0, sizeof(num));
memset(belong , 0, sizeof(belong));
memset(degree, 0, sizeof(degree));
}
int main()
{
int T, p, u, v, choose;
init();
scanf("%d %d", &n, &p);
for(int i = 1; i <= p; i++)
{
scanf("%d %d", &u, &v);
add(u, v);
}
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 = e[j].next){
int v = e[j].v;
if(belong[i] != belong[v]){
degree[belong[i]]++;
}
}
}
int flag = 0, pos = 0;
for(int i = 1; i <= Bcnt; i++){
if(degree[i] == 0){
flag++;
pos = i;
}
}
if(flag > 1)printf("0\n");
else printf("%d\n", num[pos]);
return 0;
}
题目:P2863 [USACO06JAN]牛的舞会The Cow Prom
代码:
#include<stdio.h>
#include<iostream>
#include<stack>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 10010;
const int N = 50010;
stack<int>s;
int n, m, idx = 0, k = 1, Bcnt = 0;
int head[N];
int ins[maxn];
int dfn[maxn], low[maxn];
int belong[maxn];
int num[maxn];
struct edge
{
int v, next;
} e[N];
void add(int u, int v)
{
e[k].v = v;
e[k].next = head[u];
head[u] = k++;
}
void tarjan(int u)
{
int v;
dfn[u] = low[u] = ++idx;
ins[u] = 1;
s.push(u);
for(int i = head[u]; i != -1; i = e[i].next)
{
v = e[i].v;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(ins[v])low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
Bcnt++;
do
{
v = s.top();
s.pop();
ins[v] = 0;
belong[v] = Bcnt;
num[Bcnt]++;
}
while(u != v);
}
}
void init()
{
idx = 0, k = 1, Bcnt = 0;
memset(ins, 0, sizeof(ins));
memset(dfn, 0, sizeof(dfn));
memset(low, 0,sizeof(low));
memset(head, -1, sizeof(head));
memset(num, 0, sizeof(num));
memset(belong, 0, sizeof(belong));
}
int main()
{
int T, p, u, v, choose;
init();
scanf("%d %d", &n, &p);
for(int i = 1; i <= p; i++)
{
scanf("%d %d", &u, &v);
add(u, v);
}
for(int i= 1; i <= n; i++)if(!dfn[i])tarjan(i);
int ans = 0;
for(int i = 1; i <= Bcnt; i++)
{
if(num[i] > 1)ans++;
}
printf("%d\n", ans);
return 0;
}
缩点:
题目:P3387 【模板】缩点
代码:
#include<stdio.h>
#include<iostream>
#include<stack>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 10010;
const int N = 500100;
stack<int>s;
int n, m, idx = 0, k = 1, Bcnt = 0, cn = 0, ans;
int head[N], head1[N];
int ins[maxn];
int dfn[maxn], low[maxn];
int belong[maxn], point[maxn], w[maxn], degree[maxn];
int dis[maxn];
struct edge
{
int v, next;
} e[N];
struct edge_
{
int v, next;
} e_[N];
void add(int u, int v)
{
e[k].v = v;
e[k].next = head[u];
head[u] = k++;
}
void add_(int u, int v)
{
e_[cn].v = v;
e_[cn].next = head1[u];
head1[u] = cn++;
}
void tarjan(int u)//强连通分量
{
int v;
dfn[u] = low[u] = ++idx;
ins[u] = 1;
s.push(u);
for(int i = head[u]; i != -1; i = e[i].next)
{
v = e[i].v;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(ins[v])low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
Bcnt++;
do
{
v = s.top();
s.pop();
ins[v] = 0;
belong[v] = Bcnt;
}
while(u != v);
}
}
void init()
{
idx = 0, k = 1, Bcnt = 0, cn = 1, ans = -1;
memset(head1, -1,sizeof(head1));
memset(head, -1, sizeof(head));
}
void TopSort()//拓扑排序
{
queue<int>q;
for(int i = 1; i <= Bcnt; i++){
if(!degree[i])q.push(i);
dis[i] = w[i];
}
while(!q.empty()){
int x = q.front();
q.pop();
for(int i = head1[x]; i != -1; i = e_[i].next){
int v = e_[i].v;
dis[v] = max(dis[v], dis[x]+w[v]);
degree[v]--;
if(degree[v] == 0)q.push(v);
}
}
for(int i = 1; i <= Bcnt; i++){
ans = max(ans, dis[i]);
}
}
int main()
{
int T, p, u, v, choose;
init();
scanf("%d %d", &n, &p);
for(int i = 1; i <= n; i++)
{
scanf("%d", &point[i]);
}
for(int i = 1; i <= p; i++)
{
scanf("%d %d", &u, &v);
add(u, v);
}
for(int i= 1; i <= n; i++)if(!dfn[i])tarjan(i);
for(int i = 1; i <= n; i++)
{
int v;
for(int j = head[i]; j != -1; j = e[j].next)
{
v = e[j].v;
if(belong[i] != belong[v])//缩点
{
add_(belong[i], belong[v]);
degree[belong[v]]++;
}
}
w[belong[i]] += point[i];
}
TopSort();
printf("%d\n", ans);
return 0;
}
题目:P1262 间谍网络
思路:本题的思路就是,如果当这个人不能被贿赂并且这个人跟其他人没有关系,那么就是NO。剩下的就是利用缩点,把环缩成一个点,那么这个点的最小值就是整个点的价值。
代码:
#include<stdio.h>
#include<iostream>
#include<stack>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 10010;
const int N = 50010;
const int INF = 1e9+100;
stack<int>s;
int n, m, idx = 0, k = 1, Bcnt = 0;
int head[N];
int ins[maxn];
int dfn[maxn], low[maxn];
int belong[maxn];
int num[maxn];
int degree[maxn], money[maxn];
struct edge
{
int v, next;
} e[N];
void add(int u, int v)
{
e[k].v = v;
e[k].next = head[u];
head[u] = k++;
}
void tarjan(int u)
{
int v;
dfn[u] = low[u] = ++idx;
ins[u] = 1;
s.push(u);
for(int i = head[u]; i != -1; i = e[i].next)
{
v = e[i].v;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(ins[v])low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
Bcnt++;
do
{
v = s.top();
s.pop();
ins[v] = 0;
belong[v] = Bcnt;
num[Bcnt] = (num[Bcnt], money[v]);//更新环的最小值
}
while(u != v);
}
}
void init()
{
idx = 0, k = 1, Bcnt = 0;
memset(ins, 0, sizeof(ins));
memset(dfn, 0, sizeof(dfn));
memset(low, 0,sizeof(low));
memset(head, -1, sizeof(head));
memset(num, 0, sizeof(num));
memset(belong, 0, sizeof(belong));
}
int main()
{
int T, p, u, v, choose;
init();
scanf("%d %d", &n, &p);
for(int i = 1; i <= n; i++)money[i] = INF;
for(int i = 1; i <= p; i++)
{
int pos, val;
scanf("%d %d", &pos, &val);
money[pos] = val;
}
int m;
scanf("%d", &m);
for(int i = 1; i <= m; i++)
{
scanf("%d %d", &u, &v);
add(u, v);
}
for(int i= 1; i <= n; i++)if(!dfn[i] && money[i] != INF)tarjan(i);
for(int i = 1; i <= n; i++)
{
if(!dfn[i]) //如果这个人没有被贿赂,或者跟其他人没有关系
{
printf("NO\n");
printf("%d\n", i);
return 0;
}
}
int ans = 0;
for(int i = 1; i <= n; i++)
{
for(int j = head[i]; j != -1; j = e[j].next)
{
int v = e[j].v;
if(belong[i] != belong[v])
{
degree[belong[v]]++;
}
}
}
for(int i = 1; i <= Bcnt; i++)
{
if(!degree[i]) //利用缩点,找到入度为0的点,
{
ans += num[i];
}
}
printf("YES\n");
printf("%d\n", ans);
return 0;
}
割点:
题目:P3388 【模板】割点(割顶)
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 20010;
const int N = 200100;
int head[N];
int dfn[maxn], low[maxn], k = 0, tot = 0, root, cut[maxn];
struct Node
{
int v, next;
} e[N];
void add(int u, int v)
{
e[k].v = v;
e[k].next = head[u];
head[u] = k++;
}
void Tarjan(int x)
{
dfn[x] = low[x] = ++tot;
int flag = 0;
for(int i = head[x]; i != -1; i = e[i].next)
{
int y = e[i].v;
if(!dfn[y])
{
Tarjan(y);
low[x] = min(low[x], low[y]);
if(dfn[x] <= low[y] && (x != root || ++flag > 1))cut[x] = 1;
}
else low[x] = min(low[x], dfn[y]);
}
}
int main()
{
memset(head, -1, sizeof(head));
int n, m, a, b;
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; i++)
{
scanf("%d %d", &a, &b);
add(a, b);
add(b, a);
}
for(int i = 1; i <= n; i++)
{
if(!dfn[i])root = i, Tarjan(i);
}
int cn = 0;
for(int i = 1; i <= n; i++)
{
if(cut[i])cn++;
}
printf("%d\n", cn);
for(int i = 1; i <= n; i++)
{
if(cut[i])printf("%d ", i);
}
printf("\n");
}
题目:Network of Schools POJ - 1236
思路:第一问就是问你最少给多少人发信息,所以利用缩点,然后寻找入度为0的点就是第一问的答案。第二问,最少增加多少条边,这个图示强联通图,所以就是缩点之后,找到入度和初度为0的最大值。
代码:
#include<stdio.h>
#include<iostream>
#include<stack>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 10010;
const int N = 500100;
stack<int>s;
int n, m, idx = 0, k = 1, Bcnt = 0, cn = 0, ans;
int head[N], head1[N];
int ins[maxn];
int dfn[maxn], low[maxn];
int belong[maxn], in[maxn], out[maxn];
struct edge
{
int v, next;
} e[N];
struct edge_
{
int v, next;
} e_[N];
void add(int u, int v)
{
e[k].v = v;
e[k].next = head[u];
head[u] = k++;
}
void add_(int u, int v)
{
e_[cn].v = v;
e_[cn].next = head1[u];
head1[u] = cn++;
}
void tarjan(int u)//强连通分量
{
int v;
dfn[u] = low[u] = ++idx;
ins[u] = 1;
s.push(u);
for(int i = head[u]; i != -1; i = e[i].next)
{
v = e[i].v;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(ins[v])low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
Bcnt++;
do
{
v = s.top();
s.pop();
ins[v] = 0;
belong[v] = Bcnt;
}
while(u != v);
}
}
void init()
{
idx = 0, k = 1, Bcnt = 0, cn = 1, ans = -1;
memset(head1, -1,sizeof(head1));
memset(head, -1, sizeof(head));
}
int main()
{
int T, p, u, v, choose;
init();
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
int v;
while(scanf("%d", &v) && v != 0)
{
add(i, v);
}
}
for(int i= 1; i <= n; i++)if(!dfn[i])tarjan(i);
for(int i = 1; i <= n; i++)
{
int v;
for(int j = head[i]; j != -1; j = e[j].next)
{
v = e[j].v;
if(belong[i] != belong[v])//缩点
{
add_(belong[i], belong[v]);
in[belong[v]]++;
out[belong[i]]++;
}
}
}
int ans1 = 0, ans2 = 0;
for(int i = 1; i <= Bcnt; i++)
{
if(!in[i])ans1++;
if(!out[i])ans2++;
}
printf("%d\n", ans1);
if(Bcnt == 1)
{
printf("0\n");
return 0;
}
ans = max(ans1, ans2);
printf("%d\n", ans);
return 0;
}