Kosaraju算法、强连通图(例C-班长竞选

既然PPT有现成的,我就不特意再写一个Kosaraju算法解释了。一会把课堂上的PPT扒上来做笔记。

目录

  • 强连通图
  • DFS序
  • Kosaraju算法
  • 例:班长竞选

强连通图

什么是强连通图?

强连通图(Strongly Connected Graph)是指在有向图G中,如果对于每
一对vi、vj,vi≠vj,从vi到vj和从vj到vi都存在路径,则称G是强连通图。有
向图中的极大强连通子图称做有向图的强连通分量

DFS序

Kosaraju算法、强连通图(例C-班长竞选_第1张图片

Kosaraju算法

Kosaraju算法、强连通图(例C-班长竞选_第2张图片

默默地感叹一句:TA的PPT实在是写得太好了!!!

例:班长竞选

大学班级选班长,N 个同学均可以发表意见
若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适
勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?

Input
本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 Output
对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。
接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!

Sample Input

2
4 3
3 2
2 0
2 1

3 3
1 0
2 1
0 2

Output
Case 1: 2
0 1
Case 2: 2
0 1 2

题解:A认为B合适,链接一条有向边A→B,B→C,有A→C,满足传递关系
实则我们可以把它视为一个强连通图问题,支持A的人就等于能够达到A
点的点和可以到达能够到达A点的点的点所在的强连通分量(这句话有点拗口
我画幅图理解吧

Kosaraju算法、强连通图(例C-班长竞选_第3张图片

由上图可以知道1是得到2和3支持的,但是3又是被5支持的,而5所在
的强连通分量,5是被4,7,6支持的,故因为传递性,所以1也会得到
5,4,6,7的支持

Codes

#include
#include
#include
using namespace std;
const int maxn=1e5;
int t,n,m,a,b,scnt,dcnt;
//t测试组数,n个点,m条边,scnt,dcnt计数第几次访问 
int vis[maxn],scn[maxn],scc[maxn],sc[maxn],c[maxn],dfn[maxn];
//dfn-dfs后序列第i个点 
//vis-是否访问过,c[i]i号点所在scc编号 
vector<int> G1[maxn],G2[maxn],G3[maxn],G4[maxn];
//原图,反图 ,入度为0的点图,最后关系图
void Init(){//疯狂初始化 
 for(int i=0;i<maxn;i++){
  G1[i].clear();G2[i].clear();
  G3[i].clear();G4[i].clear();
  c[i]=0;vis[i]=0;scn[i]=0;
  scc[i]=0;sc[i]=0;dfn[i]=0;
 }
 dcnt=0;scnt=0;
}
void dfs1(int x){//原图dfs 
 vis[x]=1;
 for(int i=0;i<G1[x].size();i++)
  if(vis[G1[x][i]]==0)
   dfs1(G1[x][i]);
 dfn[++dcnt]=x;
} 
void dfs2(int x){//反图dfs 
 c[x]=scnt;scn[scnt]++;
 for(int i=0;i<G2[x].size();i++)
  if(!c[G2[x][i]])
   dfs2(G2[x][i]);
}
void dfs3(int u,int v){//对于入度为0的点 
 vis[u]=1;
 for(int i=0;i<G3[u].size();i++){
  if(vis[G3[u][i]]==0){//判断它有木有被访问过 
   scc[v]+=scn[G3[u][i]];
   dfs3(G3[u][i],v);
  }
 }
}
void kosaraju(){//求强连通分量(SCC) 
 for(int i=0;i<n;i++)
  if(vis[i]==0)
   dfs1(i);
 for(int i=n;i>=1;i--){
  if(c[dfn[i]]==0){
   ++scnt;
   dfs2(dfn[i]);
  }
 }
}
int main(){
 ios::sync_with_stdio(false);
 cin>>t;//组数 
 for(int j=1;j<=t;j++){
  Init();//初始化 
  cin>>n>>m;
  for(int i=0;i<m;i++){
   cin>>a>>b;
   G1[a].push_back(b);//原图 
   G2[b].push_back(a);//构造反图 
  }//反图的SCC和原图的SCC是相同的 
  kosaraju();//求出联通分量 
  for(int i=0;i<n;i++){
   for(int h=0;h<G1[i].size();h++){
    if(c[i]!=c[G1[i][h]]){//将入度为0的点放入数组中 
     G4[c[i]].push_back(c[G1[i][h]]);
     G3[c[G1[i][h]]].push_back(c[i]);
    }
   }
  }
  for(int i=1;i<=scnt;i++){//将单独的点也放入最后关系图 
   if(G4[i].size()==0){//进行一次dfs 
    memset(vis,0,sizeof(vis));
    scc[i]+=scn[i]-1;
    dfs3(i,i);
   }
  }
  int tag=0,tot=0;//输出 
  for(int i=1;i<=scnt;i++)
   if(tag<scc[i])
    tag=scc[i];
  for(int i=1;i<=scnt;i++)
   if(tag==scc[i])
    sc[i]=1;
  cout<<"Case "<<j<<": "<<tag<<endl;
  for(int i=0;i<n;i++){
   if(sc[c[i]]==1){
    if(tot==0){
     cout<<i;
     tot++;
    }
    else
     cout<<" "<<i;
   }
  }
  cout<<endl;
 }
 return 0;
}

你可能感兴趣的:(C++语言程序)