D15 SCC 缩点 Tarjan 算法

D15 SCC 缩点 Tarjan 算法_哔哩哔哩_bilibili

P2812 校园网络【[USACO]Network of Schools加强版】 - 洛谷

图问题:有向无向?有环无环?连通不连通?正权负权?

1. 把SCC缩成一个点,旧点 x → 缩点 scc[x],n → cnt

2. 统计缩点的入度、出度,if(scc[x]!=scc[y])

3. 构造答案

#include<bits/stdc++.h>
using namespace std;

const int N=10010;
int n,m,a,b;
vector<int> e[N]; 
int dfn[N],low[N],tim,stk[N],top,scc[N],cnt;
int din[N],dout[N]; //SCC的入度,出度

void tarjan(int x){
  dfn[x]=low[x]=++tim; 
  stk[++top]=x;
  for(int y : e[x]){
    if(!dfn[y]){
      tarjan(y);
      low[x]=min(low[x],low[y]); 
    }
    else if(!scc[y])
      low[x]=min(low[x],dfn[y]);
  }
  if(dfn[x]==low[x]){
    ++cnt;
    while(1){
      int y=stk[top--];
      scc[y]=cnt;
      if(y==x) break;
    }
  }
}
int main(){
  cin >> n;
  for(int i=1, a; i<=n; i++)
    while(cin >> a, a) 
      e[i].push_back(a);
  for(int i=1; i<=n; i++) 
    if(!dfn[i]) tarjan(i);
    
  for(int x=1; x<=n; x++)
    for(int y:e[x])
      if(scc[x]!=scc[y]){
        din[scc[y]]++;
        dout[scc[x]]++;
      }
  int a=0, b=0;
  for(int i=1; i<=cnt; i++){
    if(!din[i]) a++;
    if(!dout[i]) b++;
  }
  cout<<a<<"\n";
  if(cnt == 1) cout<<"0";
  else cout<<max(a,b);
}

 

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

#include<bits/stdc++.h>
using namespace std;

const int N=10010;
int n,m,a,b;
vector<int> e[N]; 
int dfn[N],low[N],tot,stk[N],top,scc[N],siz[N],cnt;
int dout[N]; //SCC的出度

void tarjan(int x){
  dfn[x]=low[x]=++tot; 
  stk[++top]=x;
  for(int y:e[x]){
    if(!dfn[y]){
      tarjan(y);
      low[x]=min(low[x],low[y]); 
    }
    else if(!scc[y])
      low[x]=min(low[x],dfn[y]);
  }
  if(dfn[x]==low[x]){
    ++cnt;
    while(1){
      int y=stk[top--];
      scc[y]=cnt;
      ++siz[cnt];
      if(y==x) break;
    }
  }
}
int main(){
  cin>>n>>m;
  while(m--){
    cin>>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(int y : e[x])
      if(scc[x]!=scc[y]) 
        ++dout[scc[x]];
  int sum=0,zeros=0;
  for(int i=1; i<=cnt; i++)
    if(dout[i]==0){
      sum=siz[i];
      ++zeros; //出度为0的SCC的个数
    }
  if(zeros>1) sum=0;
  cout<<sum<<endl;
}

 

P3387 【模板】缩点 - 洛谷

1. 缩点

2. 对缩点建拓扑图(注意编号逆序)

3. 对拓扑图求最长路

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int n,m,a,b;
vector<int> e[N],ne[N]; 
int dfn[N],low[N],tim,stk[N],top,scc[N],cnt;
int w[N],nw[N],d[N];

void tarjan(int x){
  dfn[x]=low[x]=++tim; 
  stk[++top]=x;
  for(int y : e[x]){
    if(!dfn[y]){
      tarjan(y);
      low[x]=min(low[x],low[y]); 
    }
    else if(!scc[y])
      low[x]=min(low[x],dfn[y]);
  }
  if(dfn[x]==low[x]){
    ++cnt;
    while(1){
      int y=stk[top--];
      scc[y]=cnt;
      if(y==x) break;
    }
  }
}
int main(){
  cin>>n>>m;
  for(int i=1;i<=n;i++) cin>>w[i];
  for(int i=1;i<=m;i++){
    cin>>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;x--){ //求最长路
    if(d[x]==0) d[x]=nw[x]; //起点
    for(int y:ne[x])
      d[y]=max(d[y],d[x]+nw[y]);
  } 
  int ans=0;
  for(int i=1;i<=cnt;i++) ans=max(ans,d[i]);
  cout<<ans;
}

 

posted @ 2022-05-28 13:29  董晓  阅读(1753)  评论(0)    收藏  举报