D15 Tarjan 缩点

视频链接:https://www.bilibili.com/video/BV1qt4y1h7xu

////////Luogu P2812 校园网络
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

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

void tarjan(int x){
  dfn[x]=low[x]=++tot; 
  stk[++top]=x,instk[x]=1;
  for(int y : e[x]){
    if(!dfn[y]){//若y尚未访问
      tarjan(y);////
      low[x]=min(low[x],low[y]); 
    }
    else if(instk[y])//若y已访问且在栈中
      low[x]=min(low[x],dfn[y]);
  }
  if(dfn[x]==low[x]){//若x是SCC的根
    int y;++cnt;
    do{
      y=stk[top--];
      instk[y]=0;
      scc[y]=cnt; //y属于哪个SCC
    }while(y!=x);
  }
}
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++;
  }
  printf("%d\n",a);
  if(cnt == 1) puts("0");
  else printf("%d\n",max(a,b));
  return 0;
}

////////Luogu P2341 受欢迎的牛 G 
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

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

void tarjan(int x){
  dfn[x]=low[x]=++tot; 
  stk[++top]=x,instk[x]=1;
  for(int y : e[x]){
    if(!dfn[y]){//若y尚未访问
      tarjan(y);////
      low[x]=min(low[x],low[y]); 
    }
    else if(instk[y])//若y已访问且在栈中
      low[x]=min(low[x],dfn[y]);
  }
  if(dfn[x]==low[x]){//若x是SCC的根
    int y; ++cnt;
    do{
      y=stk[top--];
      instk[y]=0;
      scc[y]=cnt;//y属于哪个SCC
      ++siz[cnt];//当前SCC的大小 
    }while(y!=x);
  }
}
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;
  return 0;
}

//////////Luogu P3387 【模板】缩点 
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int N=10010;
int n,m;
vector<int> e[N],ne[N]; 
int dfn[N],low[N],tot;
int stk[N],instk[N],top;
int scc[N],cnt;
int w[N],nw[N],dp[N];

void tarjan(int x){
  dfn[x]=low[x]=++tot; 
  stk[++top]=x,instk[x]=1;
  for(int y : e[x]){
    if(!dfn[y]){//若y尚未访问
      tarjan(y);////
      low[x]=min(low[x],low[y]); 
    }
    else if(instk[y])//若y已访问且在栈中
      low[x]=min(low[x],dfn[y]);
  }
  if(dfn[x]==low[x]){//若x是SCC的根
    int y;++cnt;
    do{
      y=stk[top--];
      instk[y]=0;
      scc[y]=cnt; //y属于哪个SCC
    }while(y!=x);
  }
}
int main(){
  int a,b,ans=0;
  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]){
      a=scc[x],b=scc[y];
      if(a!=b) ne[a].push_back(b);
    }
  } //缩点后建拓扑图
  for(int x=cnt;x;x--){
    if(dp[x]==0) //若x为路的起点
      dp[x]=nw[x];
    for(int y : ne[x])
      dp[y]=max(dp[y],dp[x]+nw[y]);
  } //在拓扑图上逆序dp
  
  for(int i=1;i<=cnt;i++)
    ans=max(ans,dp[i]);
  cout<<ans<<endl;
  return 0;
}

 

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