POJ 3687 Labeling Balls 拓扑排序 优先队列实现

8 篇文章 0 订阅
7 篇文章 0 订阅

题意是:  有重量从1到N 的 球,和1- N的标签,对球进行标记。

给出M个关系A B 表示  标签A的球的重量< 标签B球 的重量。

要求对其排序 ,重量从小到大,  同时使标签1 尽可能的 排在前边,满足标签1的球之后,在使得标有标签二的球尽可能的靠前,依次排序。。。 如果有这样的结果输出结果如果没有输出-1;


这个题目很诡异。。。。 不仔细读题的话,很可能出错,

For each test case output on a single line the balls' weights from label 1 to labelN.

这句话是说要输出球的重量,而不是标签。。。。排完序后的顺序是标签的顺序,其下表是重量。。。


这个题目要求标签一尽可能的

通过数据来分析这个题目:

 有关系

     5 6

     6 1

     4  7

     4 2

     2 7

     9   8

构成图就是

       5-->1 -->6

       4-->2-->7

       9-->8

       3

       假如出现这种情况,我们要使得1尽可能排在前边

       那么 就应该是先确定   顺序是   

      5  1

      再使得 2 尽可能的排在前边 

      5 1 4 2

  依次确定 5 1 4 2 3 6 7 9 8

   想想这样取有什么性质??

   我们应该先取  排在 1 前边的标签 和 1  也就是这里的  5  1  ,排在一后边不能添加,因为我们要使得2 尽可能排在前边

   在取 排在 2 前边的标签  5 1  4 2 。在取3 ,

   再取 4 前边的 发现4 已经取出了 就不加操作 ,继续向后找。。。。

    

     这样的贪心取标签的过程,就是最终标签的顺序。。。。。

  觉着最有可能的拓扑排序 无法完成。。。。

    刚才给出的值是链状的结果 ,。。。

   如过形成了图。。。

  例如 :

    5-->2-->1

   4-->3-->1

  这样的情况 我们先取出 1 前 也就是 (4 3 5 2 ),但是这不是最优解。。。 最优解应该是(5 2 4 3)要使得 2 尽可能排在前边。。。。。 在图这样情况, 我们取出1 后在对 1 前边的出现的数排序即可 然后将 1 前边的 排好序后放在1 之前

这样拓扑排序也很难实现。。。。

既然先处理a,再处理a之前的出现的数。。既然每次取最小值 ,不能实现, 那每次取出最大值,剩余的就是最小值。。

这样我们先取出 1 在取出3 ,

结果就变成了

 5-->2

 4 

 这时再去 4 ,在取 2 ,在去5 。。。。

反向拓扑排序,每次取出最大值。。。用到优先队列。。

反向拓扑排序,就应该反向构图,来拓扑排序。。

拓扑排序用上优先对列 ,能够实现每次取出最大最小值的功能,可以使拓扑排序时,队列中这些入度为0 的点,按一定的顺序遍历。

附上代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;
int t;
int n,m;
int adj[220][220];
int in_deg[220];
int answer[220];
void  init(){ // 初始化邻接矩阵 和 入度、。。。。
  for(int i=0;i<=n;i++){
     for(int j=0;j<=n;j++){
       adj[i][j]=0;
     }
     in_deg[i]=0;
  }
}
bool bfs (){
   int outsum = 0;// 判断是否有环 。。 如果出队操作的个数 等于N 的话就没环,小于N 就有环。。。。
   priority_queue<int>proq;
   for(int i=1;i<=n;i++){
      if(in_deg[i]==0){
         proq.push(i);
      }  
   }
   int an_idx =n;
   while(!proq.empty()){
      int top = proq.top();
      outsum++;
      proq.pop();
      answer[top]=an_idx; // 要求的是 标签对应最小重量。     拓扑序下标就是重量。。
      an_idx--;
      for(int i=1;i<=n;i++){
        if(adj[top][i]){
	    in_deg[i]--;
	    if(!in_deg[i]){
	       proq.push(i);
	    }
	}
      }
   }
   if(outsum==n)
        return true;
   return false;
}

int main(){
   scanf("%d",&t);
   for(int i=1;i<=t;i++){
         scanf("%d%d",&n,&m);
         init();
	 bool again=false; // 判重 。。。没判重 会wa 的很惨
	 for(int j=1;j<=m;j++){
	      int  a,b;
	      scanf("%d%d",&b,&a);//反向构图。。
	      if(a!=b){
	         if(!adj[a][b]){	      
	             adj[a][b]=1;
	             in_deg[b]++;
	          }
	      }else{
	       again =true;
	      }
	 }
        
        if(again){
	    cout << -1<<endl;
	} else{
	   bool bo = bfs();
	  if(bo){
	     for(int i=1;i<n;i++){
	       printf("%d ",answer[i]);
	     }
	     printf("%d\n",answer[n]);
	  }else{
	   cout <<-1<<endl;
	  }
	}

   }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值