【总结】强连通分量

基础知识

有向图的强连通分量的Tarjan算法

例题


http://poj.org/problem?id=2186
    Tarjan+缩点
    建立有向边指向被认为受欢迎的牛。
    将Tarjan求出的强连通分量缩点,统计各点出度,若有且只有1个出度为0的,答案即为出度为0的点内包含点数,否则输出0。
    缩点之后整张图是 DAG(有向无环图),如果有强连通分量被孤立,出度为0的点数大于1。如果没有强连通分量被孤立, 若有大于1个出度为0的点(相当于路在其中某个出度为0的点处截断),不存在1个点能够被所有点到达。


POJ 1236 Network of Schools

http://poj.org/problem?id=1236
    Tarjan+缩点
    对于任务B,考虑所有入度或出度为0的点( ai , bi ),将这些点按照一定顺序连接起来,最终添加Max{ Sa , Sb }条边。
    注意在只存在一个连通分量时需要特判。


POJ 3592 Instantaneous Transference

http://poj.org/problem?id=3592
    Tarjan缩点+SPFA求最长路


UVa 10510 Cactus

原网站比较慢,丢Vjudge链接好了。
https://cn.vjudge.net/problem/UVA-10510
    根据仙人掌图的定义,边被两个环同时经过即一个非连接点被经过两次该图即不是仙人掌。Tarjan算法中找到后向边即找到一个环,从当前点向前遍历环打标记。
    在写这道题的过程中,小伙伴还发现算法中的一个疑点,为什么给Low[]取min时为什么有的是Low[u]=Min(Low[v],Low[u])而有的是Low[u]=Min(DNF[v],Low[u])呢?在尝试过将所有Low[u]=Min(DNF[v],Low[u])都改为前者之后AC代码们安然无恙之后,我们找到某博客中的一句话,在取Low[u]=Min(DNF[v],Low[u])时,只是为了让算法能够辨别这还不是一个强连通分量的根节点。


POJ 1904 King’s Quest

http://poj.org/problem?id=1904
    Tarjan算法
    王子向所有喜欢的公主连边,公主向匹配的王子连边 每个王子的答案即为同一个强连通分量中他喜欢的公主。
    因为若一个王子a1能够找到一个非原配公主b1,那么b1的原配王子也要找到一个非原配公主b2,……最终总有一个王子找到a1的原配公主。然后这就是一个美妙的环了。

———代码们———


#include<vector>
#include<cstdio>
#include<algorithm>
using namespace std;

const int sm = 1e4+10;
const int sn = 5e4+10;

int N,M,S,Bcnt,tot,top,ans,Ct,u,v;
int Bn[sm],Low[sm],Stk[sm],instk[sm];
int to[sn],nxt[sn],hd[sm],Blg[sm];
vector<int> Vc[sm];

void read(int &x) {
    char ch=getchar();x=0;
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void Add(int u,int v) {
    to[++Ct]=v,nxt[Ct]=hd[u],hd[u]=Ct;
}
int Min(int x,int y) { return x<y?x:y; }
void Tarjan(int x) {
    int j;
    Bn[x]=Low[x]=++tot;
    Stk[++top]=x,instk[x]=true;
    for(int i=hd[x];i;i=nxt[i]) {
        j=to[i];
        if(!Bn[j]) {
            Tarjan(j);
            Low[x]=Min(Low[x],Low[j]);
        } else if(instk[j]&&Low[j]<Low[x])
            Low[x]=Low[j];
    }   
    if(Low[x]==Bn[x]) {
        ++Bcnt;
        do {
            j=Stk[top--];
            instk[j]=false;
            Blg[j]=Bcnt;
            Vc[Bcnt].push_back(j);
        } while(j!=x);
    }
}
int main() {
    read(N),read(M);
    for(int i=1;i<=M;++i) 
        read(u),read(v),Add(u,v);
    for(int i=1;i<=N;++i)
        if(!Bn[i]) Tarjan(i);
    for(int i=1,Cd;i<=Bcnt;++i) {
        v=Vc[i].size(),Cd=0;
        for(int j=0;j<v;++j)
            for(int k=hd[Vc[i][j]];k;k=nxt[k])
                if(Blg[to[k]]!=i) ++Cd; 
        if(Cd==0) ++S,ans=v;
    }
    if(S==1) printf("%d\n",ans);
    else puts("0");
    return 0;
}

POJ 1236 Network of Schools

#include<vector>
#include<cstdio>
using namespace std;
const int sm = 100+5;
const int sn = 10000+5;
int N,ans,Sc,Cd,Bcnt,tot,top,Ct,Rd[sm];
int Bn[sm],Lw[sm],Stk[sm],instk[sm];
int to[sn],nxt[sn],hd[sm],Blg[sm];
int lt[sm][sm];
vector<int>Vc[sm];

void Add(int u,int v) {
    to[++Ct]=v,nxt[Ct]=hd[u],hd[u]=Ct;
}
int Min(int x,int y) { return x<y?x:y; }
int Max(int x,int y) { return x>y?x:y; }
void Tarjan(int x) {
    int j;
    Bn[x]=Lw[x]=++tot;
    instk[x]=true,Stk[++top]=x;
    for(int i=hd[x];i;i=nxt[i]) {
        j=to[i];
        if(!Bn[j]) {
            Tarjan(j);
            Lw[x]=Min(Lw[x],Lw[j]);
        } else if(instk[j]&&Lw[j]<Lw[x]) 
            Lw[x]=Lw[j];
    }
    if(Bn[x]==Lw[x]) {
        ++Bcnt;
        do {
            j=Stk[top--];
            instk[j]=false;
            Blg[j]=Bcnt;
            Vc[Bcnt].push_back(j);
        } while(j!=x);
    }
}
int main() {
    scanf("%d",&N);
    for(int i=1,x;i<=N;++i)
        while(scanf("%d",&x)==1)
            if(!x) break; else Add(i,x);
    for(int i=1;i<=N;++i)
        if(!Bn[i]) Tarjan(i);
    for(int i=1,sz;i<=Bcnt;++i) {
        sz=Vc[i].size(),Cd=0;
        for(int j=0;j<sz;++j)
            for(int k=hd[Vc[i][j]];k;k=nxt[k])
                if(Blg[to[k]]!=i) {
                    ++Rd[Blg[to[k]]],++Cd;
                    if(!lt[i][Blg[to[k]]]) 
                        lt[i][Blg[to[k]]]=1;
                }
        if(!Cd) ++Sc;
    }
    for(int i=1;i<=Bcnt;++i)
        if(!Rd[i]) ++ans;
    printf("%d\n%d\n",ans,(Bcnt!=1)?Max(ans,Sc):0);
    return 0;
}

POJ 3592 Instantaneous Transference

#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int sm = 1600+5;
const int sn = sm*3;
int T,N,M,K,top,Ct,St,Bcnt,tot,ans;
int Bn[sm],Lw[sm],Stk[sm],instk[sm],C[sm];
int fr[sn],to[sn],nxt[sn],hd[sm],Blg[sm],Val[sm],dis[sm],Que[sm];
bool vis[sm];
char Ch[45][45],ch;
vector<int> Sd[sm];
void read(int &x) {
    char ch=getchar();x=0;
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void init() {
    K=Ct=ans=Bcnt=tot=0;
    memset(hd,0,sizeof(hd));
    memset(Bn,0,sizeof(Bn));
    memset(vis,0,sizeof(vis));
    memset(dis,0,sizeof(dis));
}
void Add(int u,int v) {
    fr[++Ct]=u,to[Ct]=v,nxt[Ct]=hd[u],hd[u]=Ct;
}
int Min(int a,int b) { return a<b?a:b; }
void Tarjan(int x) {
    int j;
    Bn[x]=Lw[x]=++tot;
    Stk[++top]=x,instk[x]=true;
    for(int i=hd[x];i;i=nxt[i]) {
        j=to[i];
        if(!Bn[j]) {
            Tarjan(j);
            Lw[x]=Min(Lw[x],Lw[j]);
        } else if(instk[j]&&Lw[j]<Lw[x])
            Lw[x]=Lw[j];
    }
    if(Bn[x]==Lw[x]) {
        ++Bcnt,Val[Bcnt]=0;
        do {
            j=Stk[top--];
            instk[j]=false;
            Blg[j]=Bcnt;
            Val[Bcnt]+=C[j];
            if(j==1) St=Bcnt;
        } while(j!=x);
    }
}
void SPFA() {
    int tail=0,head=0,k,t;
    Que[++tail]=St,vis[St]=true;
    dis[St]=Val[St];
    while(head<tail) {
        k=Que[++head],vis[k]=false;
        int len=Sd[k].size();
        for(int i=0;i<len;++i) {
            t=Sd[k][i];
            if(dis[t]<dis[k]+Val[t]) {
                dis[t]=dis[k]+Val[t];
                if(!vis[t])vis[t]=true,Que[++tail]=t;
            }
        }
    }
}
int main() {
    read(T);
    while(T--) {
        init();
        read(N),read(M);
        for(int i=1;i<=N;++i,ch=getchar())
            for(int j=1;j<=M;++j)
                scanf("%c",&Ch[i][j]);
        int k;
        for(int i=1,cnt=0,u,v;i<=N;++i)
            for(int j=1;j<=M;++j) {
                k=(i-1)*M+j,C[k]=0;
                if(Ch[i][j]!='#') {
                    if(Ch[i+1][j]!='#'&&i+1<=N) Add(k,k+M);
                    if(Ch[i][j+1]!='#'&&j+1<=M) Add(k,k+1);
                    if(Ch[i][j]=='*') {
                        read(u),read(v);
                        if(Ch[++u][++v]!='#')
                            Add(k,(u-1)*M+v);
                    }
                    else C[k]=Ch[i][j]-'0';
                } 
            }
        for(int i=1;i<=N;++i)
            for(int j=1;j<=M;++j) { 
                k=(i-1)*M+j;
                if(!Bn[k]&&Ch[i][j]!='#')
                    Tarjan(k);
            }
        for(int i=1;i<=Bcnt;++i) Sd[i].clear();
        for(int i=1,a,b;i<=Ct;++i) {
            a=Blg[fr[i]],b=Blg[to[i]];
            if(a!=b) Sd[a].push_back(b);
        }
        SPFA();
        sort(dis+1,dis+Bcnt+1);
        printf("%d\n",dis[Bcnt]);
    }
    return 0;
}

UVa 10510 Cactus

#include<cstdio>
#include<vector>
using namespace std;
const int sm = 2e4+10;
int T,N,M,tot,top;
int Stk[sm],Bn[sm],Lw[sm],Fa[sm];
bool instk[sm],bck[sm],Bcnt;
vector<int> Ed[sm];
int Min(int x,int y) { return x<y?x:y; }
void read(int &x) {
    char ch=getchar();x=0;
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void initial() {
    tot=Bcnt=0;
    for(int i=1;i<=N;++i) { 
        Bn[i]=Fa[i]=instk[i]=bck[i]=0;  
        Ed[i].clear();
    }
    for(int i=1,u,v;i<=M;++i) {
        read(u),read(v);
        Ed[u+1].push_back(v+1);
    }
    while(top) Stk[top--]=0;
}
bool Back(int u,int v) {
    while(Fa[u]!=v) {
        if(bck[u]) return 0;
        bck[u]=1,u=Fa[u];
        if(Fa[u]==v) {
            if(bck[u]) return 0;
            bck[u]=1;
        } 
    }
    return 1;
}
bool Tarjan(int x) {
    int j;
    Bn[x]=Lw[x]=++tot;
    Stk[++top]=x,instk[x]=1;
    for(int i=0;i<Ed[x].size();++i) {
        j=Ed[x][i];
        if(!Bn[j]) {
            Fa[j]=x;
            if(!Tarjan(j)) return 0;
            Lw[x]=Min(Lw[x],Lw[j]);
        } else if(instk[j]) {
            if(!Back(x,j)) return 0;
            Lw[x]=Min(Lw[x],Lw[j]);
        }
    }
    if(Bn[x]==Lw[x]) {
        if(Bcnt) return 0; Bcnt=1;
        do {
            j=Stk[top--];
            instk[j]=0;
        }while(x!=j);
    }
    return 1;
}
int main() {
    read(T);
    while(T--) {
        read(N),read(M),initial();
        puts((Tarjan(1)&&tot==N)?"YES":"NO");
    }
    return 0;
}

POJ 1904 King’s Quest

哈哈伟大的输出优化…

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int sm = 2000+5;
const int sn = 200000+2000+10;
int N,top,tot,Ct,Bcnt;
int to[sn],nxt[sn],hd[sm<<1],Ans[sm];
int Bn[sm<<1],Lw[sm<<1],Stk[sm<<1],instk[sm<<1],Blg[sm<<1];
void read(int &x) {
    char ch=getchar();x=0;
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void Out(int a) {
    if(a>9) Out(a/10); 
    putchar(a%10+'0');
}
void Add(int u,int v) {
    to[++Ct]=v,nxt[Ct]=hd[u],hd[u]=Ct;
}
int Min(int x,int y) { return x<y?x:y; }
void Tarjan(int x) {
    int j;
    Bn[x]=Lw[x]=++tot;
    Stk[++top]=x,instk[x]=true;
    for(int i=hd[x];i;i=nxt[i]) {
        j=to[i];
        if(!Bn[j]) {
            Tarjan(j);
            Lw[x]=Min(Lw[x],Lw[j]);
        }
        else if(instk[j]&&Bn[j]<Lw[x])
            Lw[x]=Bn[j];
    }
    if(Lw[x]==Bn[x]) {
        ++Bcnt;
        do {
            j=Stk[top--];
            instk[j]=false;
            Blg[j]=Bcnt;
        }while(j!=x);
    }
}
int main() {
    int k,x,len;
    read(N);
    for(int i=1;i<=N;++i) {
        read(k);
        for(int j=1;j<=k;++j)
            read(x),Add(i,x+N);
    }
    for(int i=1;i<=N;++i)
        read(x),Add(x+N,i);
    for(int i=1;i<=2*N;++i)
        if(!Bn[i]) Tarjan(i);

    for(int i=1;i<=N;++i) {
        len=0;
        for(int j=hd[i];j;j=nxt[j])
            if(Blg[i]==Blg[to[j]])
                Ans[++len]=to[j]-N;
        sort(Ans+1,Ans+len+1);
        Out(len);
        for(int j=1;j<=len;++j)         
            putchar(32),Out(Ans[j]);
        putchar(10);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值