POJ 3687 Labeling Balls <<拓扑排序

Labeling Balls

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 15960   Accepted: 4691

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 bindicating 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

-----------------------------------------------------------------------------------------------------------------

题意

有n个球,重量分别是1~n,告诉你放在a位置的球要比放在b位置的球轻,输出满足要求的排列。

坑(难)点

1.重边

2.要求重量小的尽量放在前面 

思路

是一道排序题,仔细思考之后发现是拓扑排序。为此从零学了拓扑排序,学了dfs法,和用队列的拓扑排序,发现都无法解决,最后用循环解决的。

但循环也碰到了许多问题,这题真的有毒,一开始我是从小到大放进拓扑序列,同时把a<b看作是a->b,然后从小到大放进去,但是考虑以下这种情况,3->1,2->4,按此规则排出来是3124,题目要求最优解应该是2314。

经过一番思索(搜索),正解是应该反转图关系,把a<b看作b->a,然后从大到小放进拓扑序列,这样保证了大的尽量早入队,把小的留到最后,而之前那种(从小到大排序时)则会出现大的先入队的情况。

然后,在循环上,应该每次取出一个元素就将与这个元素相连的其余元素入度减一,然后break,找下一个序号最大的入度为零的元素,而不是把同一层的元素同时塞进拓扑序列,这样亦无法满足题意。

综上所述,这题要求真的非常苛刻,非常刁钻,有毒,具体看代码吧。

AC代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=200+7;
int G[N][N],deg[N],ans[N];
int n,m;
int toposort()
{
    int cnt=0;
    for(int i=0;i<n;i++)//最多n次循环完成排序
    {
        for(int j=n;j>=1;j--)//从大到小
        {
            if(!deg[j])
            {
                ans[j]=n-cnt;//把目前最大的值赋给他
                cnt++;
                for(int k=1;k<=n;k++)
                {
                    if(G[j][k])
                    {
                        deg[k]--;//相连的点入度--
                    }
                }
                deg[j]--;//自己入度--变成负的,表明已经找过此点,保证之后不再选到
                break;
            }
        }
    }
    if(cnt!=n) return 0;//如果最终没有找到n个入度为0的点,则表明有环,排序失败
    return 1;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        int a,b;
        memset(G,0,sizeof(G));
        memset(deg,0,sizeof(deg));
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            if(G[b][a]) continue;
            G[b][a]=1;
            deg[a]++;
        }
        int flag=toposort();
        if(!flag)
        {
            cout<<-1<<endl;
            continue;
        }
        cout<<ans[1];
        for(int i=2;i<=n;i++)
        {
            cout<<" "<<ans[i];
        }
        cout<<endl;
    }
    return 0;
}
View Code

后记

第一篇博客,记录自己的acm之旅,希望自己变得厉害哈哈哈,也算通过这题对拓扑排序有了一定的了解,折腾着学了很多,这几天大概会好好装修一下嘻嘻。

猜你喜欢

转载自www.cnblogs.com/computer-luo/p/9458064.html