算法:强连通分量(SCC) Tarjan算法

强连通分量,不能再加任何一个点了,再加一个点就不是强连通了

算法:强连通分量(SCC) Tarjan算法_第1张图片

 算法:强连通分量(SCC) Tarjan算法_第2张图片

vectore[N];
int dfn[N],low[N],tot;
bool instk[N];
int scc[N],siz[N],cnt;
void tarjan(int x){
    //入x时,盖戳,入栈
    dfn[x]=low[x]=++tot;
    q.push(x);
    instk[x]=true;
    for(auto y:e[x]){
        if(!dfn[y]){//若y尚未访问
            tarjan(y);
            low[x]=min(low[x],low[y]);//回x时更新low
        }
        else if(instk[y]){//若y已被访问且在栈中
            low[x]=min(low[x],dfn[y]);//更新low
        }
    }
    //离x时,记录SCC
    if(dfn[x]==low[x]){//若x是SCC的根
        int y;
        cnt++;
        do{
            y=q.top();
            q.pop();
            instk[y]=false;
            scc[y]=cnt;//SCC编号
            siz[cnt]++;//SCC大小
        }while(y!=x);
    }
}

[USACO06JAN] The Cow Prom S - 洛谷

AC代码:

#include
#define endl '\n'
#define int long long
using namespace std;
const int N=1e4+10;
int dfn[N],low[N],tot;
bool instk[N];
int siz[N],cnt;
stackq;
vector>e(N);
int n,m;
void tarjan(int x){
    dfn[x]=low[x]=++tot;
    q.push(x);
    instk[x]=true;
    for(auto v:e[x]){
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(instk[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(dfn[x]==low[x]){
        int y;
        cnt++;
        do{
            y=q.top();
            q.pop();
            instk[y]=false;
            siz[cnt]++;
        }while(y!=x);
    }
}
void solve() {
    cin>>n>>m;
    while(m--){
        int a,b;
        cin>>a>>b;
        e[a].push_back(b);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) tarjan(i);
    }
    int ans=0;
    for(int i=1;i<=cnt;i++){
        if(siz[i]>1) ans++;
    }
    cout<>t;
    while(t--) {
        solve();
    }
    return 0;
}

 Trajan SCC缩点

算法:强连通分量(SCC) Tarjan算法_第3张图片

算法:强连通分量(SCC) Tarjan算法_第4张图片 

我们加边的时候让出度为0的点指向入度为0的点,那么只要max(din,dout)即可

AC代码:

#include
#define endl '\n'
#define int long long
using namespace std;
const int N=1e4+10;
int dfn[N],low[N],tot;
bool instk[N];
int scc[N],cnt;
int din[N],dout[N];//SCC的入度,出度
int n;
vector>e(N);
stackq;
void tarjan(int x){
    dfn[x]=low[x]=++tot;
    q.push(x);
    instk[x]=true;
    for(auto v:e[x]){
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(instk[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(dfn[x]==low[x]){
        int y;
        cnt++;
        do{
            y=q.top();
            q.pop();
            instk[y]=false;
            scc[y]=cnt;
        }while(y!=x);
    }
}
void solve() {
    cin>>n;
    for(int i=1,a;i<=n;i++){
        cin>>a;
        while(a){
            e[i].push_back(a);
            cin>>a;
        }
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) tarjan(i);//一些点可能走不到,即图不连通
    }
    for(int x=1;x<=n;x++){//枚举n个点
        for(int y:e[x]){//枚举点x的邻边,x指向y
            if(scc[x]!=scc[y]){//如果x和y所在的强连通分量不一样,即不在同一个强连通分量之内
                din[scc[y]]++;//y所在的强连通分量的入度++
                dout[scc[x]]++;//x所在的强连通分量的出度++
            }
        }
    }
    int a=0,b=0;
    for(int i=1;i<=cnt;i++){
        if(!din[i]) a++;//a表示缩点后入度为0的个数
        if(!dout[i]) b++;//b表示缩点后出度为0的个数
    }
    cout<>t;
    while(t--) {
        solve();
    }
    return 0;
}

[USACO03FALL / HAOI2006] 受欢迎的牛 G - 洛谷 

算法:强连通分量(SCC) Tarjan算法_第5张图片 AC代码:

#include
#define endl '\n'
#define int long long
using namespace std;
const int N=1e4+10;
int dfn[N],low[N],tot;
bool instk[N];
int scc[N],siz[N];
int cnt;
int dout[N];
vector>e(N);
stackq;
int n,m;
void tarjan(int x){
    dfn[x]=low[x]=++tot;
    q.push(x);
    instk[x]=true;
    for(auto v:e[x]){
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(instk[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(dfn[x]==low[x]){
        int y;
        cnt++;
        do{
            y=q.top();
            q.pop();
            instk[y]=false;
            scc[y]=cnt;
            siz[cnt]++;
        }while(y!=x);
    }
}
void solve() {
    cin>>n>>m;
    for(int i=0;i>a>>b;
        e[a].push_back(b);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) tarjan(i);
    }
    for(int x=1;x<=n;x++){
        for(auto y:e[x]){
            if(scc[x]!=scc[y]){
                dout[scc[x]]++;
            }
        }
    }
    int sum=0;
    int cnt1=0;
    for(int i=1;i<=cnt;i++){
        if(dout[i]==0){
            sum=siz[i];
            cnt1++;
        }
    }
    if(cnt1>1) sum=0;
    cout<>t;
    while(t--) {
        solve();
    }
    return 0;
}

【模板】缩点 - 洛谷

先缩点转化成一个无环图

算法:强连通分量(SCC) Tarjan算法_第6张图片

 

团号逆序是拓扑序,因为我们给强连通分量标号的时候是从1开始标的,于是团号小的在拓扑序的末端,这样从大到小枚举团号即为拓扑序,保证是线性的,这样dp的话才能满足无后效性

AC代码:

#include
#define endl '\n'
#define int long long
using namespace std;
const int N=1e4+10;
int dfn[N],low[N],tot;
bool instk[N];
int scc[N],cnt;
int w[N],nw[N];
int n,m;
vector>e(N),ne(N);
stackq;
int dp[N];
void tarjan(int x){
    dfn[x]=low[x]=++tot;
    q.push(x);
    instk[x]=true;
    for(auto v:e[x]){
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(instk[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(dfn[x]==low[x]){
        int y;
        cnt++;
        do{
            y=q.top();
            q.pop();
            instk[y]=false;
            scc[y]=cnt;
        }while(y!=x);
    }
}
void solve() {
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>w[i];
    for(int i=0;i>a>>b;
        e[a].push_back(b);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) tarjan(i);
    }
    for(int x=1;x<=n;x++){
        nw[scc[x]]+=w[x];//新点的权值
        for(int y:e[x]){
            if(scc[x]!=scc[y]){
                ne[scc[x]].push_back(scc[y]);
            }
        }
    }//缩点后建拓扑图
    for(int x=cnt;x>=1;x--){
        if(dp[x]==0){//若x为路的起点
            dp[x]=nw[x];
        }
        for(auto y:ne[x]){
            dp[y]=max(dp[y],dp[x]+nw[y]);
        }
    }
    int ans=0;
    for(int i=1;i<=cnt;i++) ans=max(ans,dp[i]);//可能图不连通,有多个强连通分量
    cout<>t;
    while(t--) {
        solve();
    }
    return 0;
}

你可能感兴趣的:(算法学习,算法,c++)