POJ 3687 Labeling Balls (逆向拓扑排序)

Labeling Balls

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 14809 Accepted: 4339

Description

Windy has N balls of distinct weights from 1 unit to N units. Now he tries to label them with 1 to N in such a way that:

  1. No two balls share the same label.
  2. The labeling satisfies several constrains like “The ball labeled with a is lighter than the one labeled with b”.

Can you help windy to find a solution?

Input

The first line of input is the number of test case. The first line of each test case contains two integers, N (1 ≤ N ≤ 200) and M (0 ≤ M ≤ 40,000). The next M line each contain two integers a and b indicating the ball labeled with a must be lighter than the one labeled with b. (1 ≤ a, b ≤ N) There is a blank line before each test case.

Output

For each test case output on a single line the balls’ weights from label 1 to label N. If several solutions exist, you should output the one with the smallest weight for label 1, then with the smallest weight for label 2, then with the smallest weight for label 3 and so on… If no solution exists, output -1 instead.

Sample Input

5

4 0

4 1
1 1

4 2
1 2
2 1

4 1
2 1

4 1
3 2

Sample Output

1 2 3 4
-1
-1
2 1 3 4
1 3 2 4

Source

POJ Founder Monthly Contest – 2008.08.31, windy7926778

题意:给定N个球,N个球有各自的编号,也有各自的重量,任意两个编号不同的球的重量不相等。

输入:给出T组测试数据,每组数据包括第一行的N,W。接下来有N行,每行有两个正整数a,b,意为a号球比b号球轻。若答案有多种,则输出的答案必须让编号为1的球重量尽量轻,接着是编号为2的球重量尽量轻,一直到编号为N的球尽量轻。   但是这个题目的坑点在于,最后输出的不是各个球从轻到重的编号,而是从1到N各个球是第几重的,或者说输出meigeqiudezhongliang。如果按照第一种理解,测试数据是对的,但是会一直WA。

题目坑点:

1、题意,是从1-N输出每个球的重量,而非从轻到重输出每个球的编号。所以不能直接输出拓扑排序的结果,要进行数组处理,转换为ans[i]。

2、当a=b时,是非法的,要输出-1。

3、不允许存在环,如a<b,b>a。这样会造成死循环,所以在拓扑排序的算法中要添加判断死循环的条件。可能不能在输入时判断,因为后面的数据可能会对其造成影响,这里目前还没弄懂原因,一直WA。

4、当出现重复边时,要进行判断,当做只出现一次来对待。如a<b,a<b。

5、由于是要让编号小的尽量轻,而正向拓扑排序只能使得目前入度为0,也就是局限在当前关系上的小的那个数设置为小的重量或者大的重量,但是并没有考虑其他的。若是反向,则可以优先选出逻辑上最重的,反正逻辑上是最重得了没得跑了,那么就给他安排个最重的,由N-1。测试本坑点的一个很好的例子在下面的第2组测试数据给出,可以自行尝试。


几组测试用例:

7
10 5
4 1
8 1
7 8
4 1
2 8

5 4
5 1
4 2
1 3
2 3

4 0

4 1
1 1

4 2
1 2
2 1

4 1
2 1

4 1
3 2

输出:

5 1 6 2 7 8 3 4 9 10

2 4 5 3 1

1 2 3 4

-1

-1

2 1 3 4

1 3 2 4

代码:

#include <iostream> #include<stdio.h> #include<math.h> #include<string.h> #include<algorithm> #include<stdlib.h> using namespace std; int map[210][210];//用来取消入度的,如果map[i][j]=1,则删除j的入度 int indegree[210];//标记入度数 int flag=0; //测试是否为环(死循环)、重边、自环的标志 int ans[210];//最终结果 void Toposort(int n) //逆向拓扑排序 { int i,j,t=n; while(t>=1) //t代表由重到轻赋值重量,使得编号小的尽量轻 { for(i=n;i>=1;i--) //由N-1进行查找,使得编号小的尽量轻 if(indegree[i]==0) //如果入度为0,由于是逆向拓扑,说明这个在逻辑上是最大的(n到i已经检查过了) { for(j=1;j<=n;j++) //查找它的出度边,删除出度 { if(map[i][j]==1) //找到了,对应结点的入度-- indegree[j]--; } indegree[i]--; //自己的入度也-- ans[i]=t--; //由于每个点只能出现在这里一次,所以用的i。 break; } if(i==0) {flag=1;break;} //如果for循环结束,还是有边缺失,证明存在环,最后剩余至少两个入度不为0的。则退出死循环,否则一直时间超限 } return ; } int main() { int t,n,w,i,a,b,j; scanf("%d",&t); while(t--) { flag=0; //初始化 memset(indegree,0,sizeof(indegree)); memset(map,0,sizeof(map)); scanf("%d %d",&n,&w); while(w--) { scanf("%d %d",&a,&b); if(a!=b) //测试是否自环 { if(map[b][a]==0) //测试是否出现a<b,a<b的重边,入度只加1次 {map[b][a]=1;indegree[a]++;} } else //出现自环 {flag=1;} } if(!flag) { Toposort(n); if(flag==1) //如果陷入死循环则输出-1 { printf("-1\n");continue; } for(i=1;i<=n-1;i++) //输出最终结果,貌似最后有空格也能AC,这里最后没有空格 { printf("%d ",ans[i]); } printf("%d\n",ans[i]); } else printf("-1\n"); } return 0; } 

最后,贴一个网上的一个很好的解析,看不懂看这个:
POJ 3687解析

    原文作者:拓扑排序
    原文地址: https://blog.csdn.net/anan15151529/article/details/77255958
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞