题目链接:https://vjudge.net/problem/POJ-2553
如果不会tarjan算法,推荐博客:https://blog.csdn.net/mengxiang000000/article/details/51672725
题意大意:第一行输入一个n(n==0时结束程序),和一个m分别代表点数和边数,接下来一行有2*m个数字,每两个数字u,v表示一条有向边,即u可以到达v。
假如一个点a可以到b,c,d,e这些点,并且这些点也可以到达a点,则a点就是符合要求的点,即一个点可以到达的其他所有点都可以反过来到达这个点,则这个点就是符合要求的点,当然,如果这个点不能到其他任何点,那么这个点也是符合要求的,,现在就让我们找出所有符合要求的点,并且升序输出。。
所以我们的思路先用tarjan算法进行缩点操作,求出所有的强连通分量并且染色,这样的话一个强连通分量里所有的点之间都是相互可达的,而如果某个强连通分量里的点可以到其他强连通分量里的点,即这个强连通分量缩成一个点之后的出度不为0,那么这个强连通分量里所有的点都是不符合要求的,因为另一个强连通分量里的所有点必然不可能到达第一个强连通分量,否则两个强连通分量就可以合成一个了。所以我们的任务就是找到出度为0的所有强连通分量,然后把这些强连通分量里的点排序输出。
代码:
#include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<map> #include<stack> #include<cmath> #include<vector> #include<fstream> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; #define eps 1e-8 #define ll long long #define INF 0x3f3f3f3f #define maxn 5005 /*struct point{ int u,w; }; bool operator <(const point &s1,const point &s2) { if(s1.w!=s2.w) return s1.w>s2.w; else return s1.u>s2.u; }*/ int n,k,m,t; int ans,top,cnt,color_num,time; int dfn[maxn],vis[maxn],s[maxn],color[maxn],head[maxn],low[maxn]; //dfn[i]表示是第几个单位时间到i点,vis[i]表示i点是否在栈中,s是栈,color[i]表示点i的颜色,即它是属于哪个强连通分量 //low[i]用来判断强连通分量(low[i]==dfn[i]) int out[maxn],ss[maxn];//out[i]表示编号为i的强连通分量的出度,ss里面储存出度为0的强连通分量里面的点, //之后把ss数组 里面的点排序之后输出 struct node{ int v,next; }edge[maxn*10]; void init() { memset(vis,0,sizeof(vis)); memset(head,-1,sizeof(head)); memset(color,0,sizeof(color)); memset(out,0,sizeof(out)); memset(dfn,0,sizeof(dfn)); ans=top=cnt=color_num=time=0; } void add(int u,int v) { edge[++cnt].v=v; edge[cnt].next=head[u]; head[u]=cnt; } void tarjan(int u)//找强连通分量 { dfn[u]=low[u]=++time; vis[u]=1; s[++top]=u; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(!dfn[v])//第一次到v点,则继续深搜 tarjan(v); low[u]=min(low[u],low[v]);//将最小值向上传递 } if(low[u]==dfn[u]) { ans++; color_num++; int v; do{ v=s[top--];//出栈 vis[v]=0; color[v]=color_num;//染色,同一个强连通分量的所有点都是同一个编号 }while(u!=v); } } void solve() { for(int i=1;i<=n;i++) { if(!dfn[i]) { tarjan(i); } } for(int u=1;u<=n;u++)//枚举所有点 { for(int i=head[u];i!=-1;i=edge[i].next)//枚举所有和u点相邻的边 { int v=edge[i].v; int a=color[u]; int b=color[v]; if(a!=b)//如果u和v的颜色不同,即a,b不同,则out[a]的出度++ { out[a]++; } } } int num=0; for(int i=1;i<=n;i++) { if(out[color[i]]==0)//储存出度为0的强连通分量里面的点 ss[num++]=i; } sort(ss,ss+num);//排序 for(int i=0;i<num;i++)//输出结果 { if(i!=num-1) cout<<ss[i]<<' '; else cout<<ss[i]<<endl; } } int main() { while((cin>>n>>m)&&n) { init(); for(int i=0;i<m;i++) { int u,v; cin>>u>>v; add(u,v); } solve(); } return 0; }