版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37275680/article/details/82118407
此篇文章的整理仅用于学习记录,供日后复习,大部分内容来源于北大郭炜老师整理的课件。
1.POJ2186:Popular Cows
题意:给定一个有向图,求有多少顶点是由任何顶点出发都可达的。
有用的定理:有向无环图中唯一出度为0的点,一定可以有任何点出发均可达(由于无环,所以从任何点出发往前走必然终止于一个出度为0的点)。
解题思路:1.求出所有强连通分量;
2.每个强连通分量缩成一个点,则形成一个有向无环图DAG。
3.DAG上面如果有唯一的出度为0的点,则该点能被所有的点可达。那么该点所代表的连通分量上的所有的原图中的点,都能被原图中的所有点可达,则该连通分量的点数,就是答案。
4.DAG上面如果有不止一个出度为0的点,则这些点互相不可达,原问题无解,答案为0.
缩点的时候不一定要构造新图,只要把不同强连通分量的点染不同颜色,然后考察各种颜色的点有没有连到别的颜色的边即可(即其对应的缩点后的DAG图上的点是否有出边)。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int maxn=1e4+10;
const int maxm=5e4+10;
int n,m,dfn[maxn],low[maxn],tot=0,stck[maxn],r=0,cnt=0,pos[maxn],sum[maxn];
int outd[maxn]; //出度
vector<int> sons[maxn];
void tarjan(int u){
dfn[u]=low[u]=++tot;
stck[++r]=u;
for(int i=0;i<sons[u].size();i++){
int v=sons[u][i];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(!pos[v]){ //pos[v]表明该强连通分量还在栈中
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]){
int len=r;
cnt++; //染色编号
while(stck[r]!=u) pos[stck[r--]]=cnt; //缩点
pos[u]=cnt;
r--;
sum[cnt]=len-r; //该强连通分量中总的结点数
//cout<<"len-r="<<len-r<<",len="<<len<<",r="<<r<<endl;
//printf("sum[%d]=%d\n",cnt,sum[cnt]);
}
}
int main(){
scanf("%d%d",&n,&m);
int x,y;
for(int i=0;i<m;i++){
scanf("%d%d",&x,&y);
sons[x].push_back(y);
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=n;i++){
for(int j=0;j<sons[i].size();j++){
int v=sons[i][j];
if(pos[i]!=pos[v]) outd[pos[i]]++;
}
}
int ans=0;
bool flag=false;
//cout<<"cnt="<<cnt<<endl;
for(int i=1;i<=cnt;i++){
//cout<<"sum["<<i<<"]="<<sum[i]<<endl;
if(outd[i]==0) {
if(!flag){
ans=sum[i];
flag=true;
}else{
ans=0;
break;
}
}
}
printf("%d\n",ans);
return 0;
}