poj3687 Labeling Balls【反向建图拓扑排序】

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, bN) 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

题意:有n个球,分别编号为1到n,并且每个球有对应的重量,输入时,每行输入的两个数,表示“编号”为a的球的重量小于“编号为b的球的重量”,分别输出1到n编号对应的球的重量,尽量保证编号较小的球重量也较小。注意仔细理解题:读入的时候是球的编号,输出的是对应编号的球的重量,看到dicuss区的数据才发现自己题目都读错了。

思路:这道题难点在于,怎么保证编号小的球分到小重量,按照之前的拓扑排序套路,正向进行的话,会使较大的标签分到较小的重量(如果不理解,可以自己写一个正向拓扑程序进行验证),所以,反向拓扑,将大重量分给大标签,实现思路是:读入a,b时,令重量较轻的a标签入度+1,因为入度为0的标签先进行赋重量,就保证了重量较重的b标签赋得大值,并且,在排序过程中,每给一个入度为0的标签赋值and去掉所有出边时,就要break,然后重新从n到1开始遍历,保证每个较大标签都赋上大重量(因为入度为0的标签去掉出边后,有可能有比这个标签更大的标签入度也变为了0)。一开始看别人博客的时候,没有明白为什么break,直到自己把有环和无环的情况给输出以后才发现,原来设置的如此巧妙啊~~厉害厉害~~~

给一组discuss区的数据,就是这组数据让我重新认清这道题~

2

5 4
5 1
4 2
1 3
2 3

10 5
4 1
8 1
7 8
4 1
2 8
ans:
2 4 5 3 1        逆向建图
5 1 6 2 7 8 3 4 9 10  重边的话就输出 -1

#include<stdio.h>
#include<queue>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 210;
int vis[maxn][maxn],in[maxn],num[maxn];
int Top_sort(int n)
{
	int count = 0,flag1=0,i,j;
	int w = n;
	while(w >= 1 ) 
	{
		for( i = n; i >= 1; i --)//从编号较大开始到较小的值进行遍历 
		{
			if(!in[i])
			{
				num[i] = w--;//将大重量赋值给当前编号值 
				for(int j = n; j >= 1; j --)
				{
					if(vis[i][j])
						in[j] --;
				}
				in[i]--;
				break;//break在此设置很巧妙,每个入度为0的编号赋值一次,如果没有环,那么w减少到0时(此时i的最小值才为1)
                                     //会跳出while循环,而如果有环的出现,会陷入死循环,即i的值会一直为0
                        }}if(i == 0)//陷入死循环时,说明有环return -1;}return 0;}int main(){int n,m;int t;int n1,n2,flag,sum;scanf("%d",&t);while(t--){scanf("%d%d",&n,&m);memset(vis,0,sizeof(vis));memset(in,0,sizeof(in));flag = 0;for(int i = 0; i < m; i ++){scanf("%d%d",&n1,&n2);if(n1 == n2)flag = -1;if(!vis[n2][n1]){vis[n2][n1] = 1;in[n1]++;}}sum = Top_sort(n);if(sum == -1||flag == -1){printf("-1\n");continue;}for(int i = 1; i <= n; i ++)printf("%d ",num[i]);printf("\n");}return 0;}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值