贴标签

罗老师正在给一堆球贴标签,总共有N个球,要给他们贴1-N的编号,不能重复。
罗老师首先将这N个球排成一排,排好后,突然想给自己找点麻烦(无聊),就想出了M条限制条件,每个条件是:
A B
表示第A个球的编号要小于第B个球的编号。
当然,限制条件会造成有时答案有很多种,那么罗老师希望贴好标签后的球字典序最小;也有可能答案不存在,那么就输出-1。

输入
首先输入T,表示有T组测试数据
然后每组测试数据首先输入N, M
接下来M行每行输入A B,表示第A个球编号要小于第B个球

输出
如果答案存在,输出最小字典序(数字字典序,也就是优先小数字在前,比如 22 在 111前)不存在就输出”-1”

样例输入
5
4 0
4 1
1 1
4 2
1 2
2 1
4 1
2 1
4 1
3 2
样例输出
1 2 3 4
-1
-1
2 1 3 4
1 3 2 4
提示
【数据规模和约定】
1<=N<=200 0<=M<=40000 可能有重边

题意:n个重量为1n的球,给定一些编号间的重量比较关系,现在给每个球编号,在符合条件的前提下使得编号小的球重量小。(先保证1号球最轻,其次2号……)
分析:拓扑排序,注意根据题的要求,要先保证1号球最轻,如果我们由轻的向重的连边,然后我们依次有小到大每次把重量分给一个入度为0的点,那么在拓扑时我们面对多个入度为0的点,我们不知道该把最轻的分给谁才能以最快的速度找到1号(使1号入度为0),并把当前最轻的分给1号。所以我们要由重的向轻的连边,然后从大到小每次把一个重量分给一个入度为0的点。这样我们就不用急于探求最小号。我们只需要一直给最大号附最大值,尽量不给小号赋值,这样自然而然就会把轻的重量留给小号。
注意重边。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
int t,n,m,x,y,tot;
int p[205][205],a[205][205],c[205],ru[205];
int head[205],Next[40005],to[40005];
void add(int x,int y)
{
    tot++;
    Next[tot]=head[x];
    to[tot]=y;
    head[x]=tot;
}
void tuopu()
{
    int w=n;
    while(1) 
    {
        int t=0,find=0;
        for(int i=n;i>=1;i--) 
        if(ru[i]==0) 
        {
            find=1;
            c[i]=w;
            w--;
            ru[i]--;
            for(int j=head[i];j!=-1;j=Next[j]) ru[to[j]]--;
            break;
        }
        if(!find) break;    
    }
    for(int i=1;i<n;i++) printf("%d ",c[i]);
    printf("%d\n",c[n]);
}
int floyd()
{
    for(int k=1;k<=n;k++) 
    for(int i=1;i<=n;i++) 
    for(int j=1;j<=n;j++) 
    if(a[i][k]&&a[k][j]) a[i][j]=1;
    for(int i=1;i<=n;i++) 
    for(int j=1;j<=n;j++) 
    if(a[i][j]&&a[j][i]) return -1;
    return 0;
}
int main()
{
    cin>>t;
    while(t--) 
    {
        cin>>n>>m;
        tot=0;
        memset(p,0,sizeof(p));
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++) 
        {
            head[i]=-1;
            ru[i]=0;
        }
        for(int i=1;i<=m;i++) 
        {
            scanf("%d%d",&x,&y);
            if(p[y][x]==0)
            {
                add(y,x);
                ru[x]++;
            }
            p[y][x]=1;
            a[y][x]=1;
        }
        if(floyd()==-1) printf("-1\n"); else tuopu();
    }
    return 0;
}   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值