POJ 3687 Labeling Balls(拓扑排序)

273 篇文章 0 订阅
112 篇文章 0 订阅

POJ 3687 Labeling Balls(拓扑排序)

http://poj.org/problem?id=3687

题意:给你N个球,编号从1到N.且这N个球的重量各不相同,且他们的重量正好是从1到N个单位.现在还给出了M个关系,每个关系描述了两个不同编号球之间的重量关系.现在要你输出这N个球可能的重量,从1号球到N号球.如果存在多解,则输出1号球最轻的解.如果1号球最轻时依然有多解,那么输出2号球最轻的解.依此类推.

分析:

       其实这道题目我们可以理解成有1到N个孔,我们要把N个球放进去.如果2号球房间了5号孔,那么2号球重5个单位.现在我们要找出一个放球的方法,使得球序列满足M条约束且球序列的字典序最小.(球序列的字典序最小就保证了1号球最轻,2号球最轻等等)

       以上分析是错误的.字典序最小的序列,并不能保证1号球最轻.

比如下面实例:

1

6 4

1 6

3 1

2 4

4 5

输出应为:23 1 4 5 6

拓扑排序字典序最小的序列是:2 3 1 4 5 6,转换成重量输出结果是3 1 2 4 5 6. 其中字典序最小的序列使得1号球的重量变成了3.

但是正确结果的拓扑排序应该是:3 1 2 4 5 6,转换成重量是:2 3 1 4 5 6.

这个序列使得1号球的重量变成了2,明显这个序列更优,但是字典序不一定最小.

       其实上面的错误解法的思想是我们每次都优先选入度为0的最小序号的球放当前最小可用的格子.这样不能保证得到的解是1号球最轻的.比如23<1我们第一步肯定先放2,然后放3,再是1.但其实我们可以先放3,1,2.这样1号球只重2个单位.

       但是如果我们每次都是优先选出度为0的最大序号球放在当前可能的最大格子里,那么我们得到的解能保证1号球是最轻的.因为当我们想放1号球的时候,还剩下没放的球比如都是比1号球要轻的(就算它们编号比1).如果某个球与1号球没关系,那么它肯定已经被放置在了一个大的格子里.

       总结上述原理就是:我们如果要放i号球的时候,我们一定要保证当前还没有被放的所有球中,只有明确比i号球轻的球或是序号<i的球.如果与i号球无关的球且序号>i的话,那么明显我应该先放这个大序号的球到大编号格子上去.

AC代码:

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=200+10;
int n,m;
int G[maxn][maxn];
int out[maxn];//出度
int ans[maxn];//ans[i]=x表示i号球重x
int main()
{
    int T; scanf("%d",&T);
    while(T--)
    {
        memset(G,0,sizeof(G));
        memset(out,0,sizeof(out));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);//从u->v的边,说明v比u重
            if(G[u][v]==0)
            {
                G[u][v]=1;
                out[u]++;
            }
        }
        int i,j;
        for(i=n;i>=1;i--)//从n号格子开始放球
        {
            for(j=n;j>=1;j--)//优先放编号大的球
                if(out[j]==0)//出度为0,表示没有比j还重的球没放了
            {
                out[j]--;//标记
                ans[j]=i;
                for(int k=1;k<=n;k++)if(G[k][j])//减少与j先关的所有点的出度
                    out[k]--;
                break;
            }
            if(j<1) break;//无出度为0的球了
        }
        if(i>=1)//还有格子没放球,说明有环
            printf("-1\n");
        else
        {
            printf("%d",ans[1]);
            for(int i=2;i<=n;i++)
                printf(" %d",ans[i]);
            puts("");
        }
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值