强连通分量Tarjan

参考博客:浅析强连通分量(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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值