可以直接见这个博客:http://blog.csdn.net/black_miracle/article/details/52164974。
对其中的几点作一些解释:
1.这个方法我们对队列中取出的元素,把仍有出度的点连接到这个点时,这个点是不会连接到多余的点的,因为:一个点出队列的时候其他没入队的点都已经连接到这个点过了,后来再一个点出队列的时候,我们拿此时没入队的点连接到它时,肯定已经连接过之前的点了,所以肯定不会增加到达的点数。(可能我也用语言讲不太清楚233。。。)
2.为什么这个人的博客说最终如果num不是0的话就表示成环了?理由如下:不妨考虑四个点的情况:3,3,3,0。最后一个点操作后,出度变为2,2,2,0。这个时候队列已经空了,然而num还不是0,说明还有点没有完成达到它所需要达到的点数(也可以这么解释:num的个数表示还没有完成点数个数任务的点的个数,num=0才表示所有的点都已经完成到达点数个数的目标了,不是0当然不能够满足题意了)。至于为什么是成环的话,因为它们不能够完成点数个数任务,所以他们必须连向别人构成环来构成目标了呗~
反正这个拓扑排序有点奥义,需要好好理解~
代码如下:
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <vector> 5 #include <set> 6 #include <queue> 7 using namespace std; 8 const int N = 1000 + 5; 9 10 struct edge 11 { 12 int u,v; 13 bool operator < (const edge &temp) const 14 { 15 return u == temp.u ? v<temp.v : u<temp.u; 16 } 17 }; 18 int a[N],inq[N],n; 19 set<edge> S; 20 21 void solve() 22 { 23 queue<int> Q; 24 int cnt = n; 25 for(int i=1;i<=n;i++) 26 { 27 if(!a[i]) 28 { 29 Q.push(i); 30 inq[i] = 1; 31 cnt--; 32 } 33 } 34 while(!Q.empty()) 35 { 36 int x = Q.front();Q.pop(); 37 for(int i=1;i<=n;i++) 38 { 39 if(!inq[i]) 40 { 41 a[i]--; 42 S.insert((edge){i,x}); 43 if(!a[i]) 44 { 45 Q.push(i); 46 inq[i] = 1; 47 cnt--; 48 } 49 } 50 } 51 } 52 if(cnt) puts("No"); 53 else 54 { 55 puts("Yes"); 56 printf("%d ",S.size()); 57 for(set<edge>::iterator it=S.begin();it!=S.end();it++) 58 { 59 edge e = *it; 60 printf("%d %d ",e.u,e.v); 61 } 62 } 63 } 64 65 int main() 66 { 67 int T;scanf("%d",&T); 68 for(int kase=1;kase<=T;kase++) 69 { 70 scanf("%d",&n); 71 S.clear();memset(inq,0,sizeof(inq)); 72 for(int i=1;i<=n;i++) scanf("%d",a+i); 73 printf("Case #%d: ",kase); 74 solve(); 75 } 76 return 0; 77 }