zoukankan      html  css  js  c++  java
  • POJ 3687 Labeling Balls 变种拓扑排序

    Labeling Balls
    Time Limit: 1000MS   Memory Limit: 65536K
    Total Submissions: 8219   Accepted: 2233

    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
    /*
    纠结了两天的 值得思考的一道题。。。。
    题目大意:
        这道题每次输入a,b的时候表示的是编号为a的球比编号为b的球轻,最后输出的是从编号 1
    	到编号 n每个小球的重量,如果存在多组解,输出使最小重量尽量排在前边的那组解,亦即 所有解中 1到 n号球的重量的字典序最小。。。。
    	所以说最后重量 1 到 n 的球的标号的字典序最小的做法是不对的。
    	才开始的时候理解题意就理解错了:
    原因: 本来想着用拓扑排序加上简单的贪心,每次从入度为0的节点中找出序号最小的,放进队列中,
    	再更新其他节点。但是 wa,这样做是不行的,不能得到最优解,
    	正向的贪心不能完全保证序号小的节点尽量排在前面。仔细思考~~~~~
    	
        注意题目中很重要的一句话:
    	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...
    	
    	
    	
    */
    //代码一:----WA ----算法思想就错了
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    const int N=201;
    int G[N][N],d[N];
    
    void toposort(int n)
    {
    	int i,j;
    	int num=0;
    	int ans[N];
    	while(num<n)
    	{
    		bool flag=false;
    		for(i=1;i<=n;++i)
    		{
    			if(d[i]==0)
    			{
    				flag=true;
    				++num;
    				ans[i]=num;
    				d[i]=-1;
    				break;
    			}
    		}
    		if(flag)
    		{
    			for(j=1;j<=n;++j)
    				if(G[i][j])
    					--d[j];
    		}
    		else
    		{
    			printf("-1\n");
    			return ;
    		}
    	}
    	for(i=1;i<n;++i)
    		printf("%d ",ans[i]);
    	printf("%d\n",ans[n]);
    }
    
    int main()
    {
    	int T;
    	scanf("%d",&T);
    	while(T--)
    	{
    		memset(G,0,sizeof(G));
    		memset(d,0,sizeof(d));
    		int n,m,a,b;
    		scanf("%d%d",&n,&m);
    		if(!m)
    		{
    			for(int i=1;i<n;++i)
    				printf("%d ",i);
    			printf("%d\n",n);
    			continue;
    		}
    		bool flag=false;
    		while(m--)
    		{
    			scanf("%d%d",&a,&b);
    			if(a==b||flag)
    			{
    				flag=true;
    				continue;
    			}
    			if(!G[a][b])
    			{
    				G[a][b]=1;
    				++d[b];
    			}	
    		}
    		if(flag)
    			printf("-1\n");
    		else
    			toposort(n);
    	}
    	return 0;
    }
    
    //代码二:AC----copy:反向建图
    /*
    解题思路:
    	将输入的n行建立有向图,如果该图产生回路,那么输出-1,如果不产生回路,将入度为0的点放进优先队列中,
    	优先队列从大到小排序,每个队列按顺序出队,当出队的数与其他点有边存在,就在相应该点的入度减一,
    	然后在判断是否入度为0,如果为0再次入队。本题一个比较容易出错的就是判重,如果输入两个相同的a和b,
    	那么如果没有判重将会输出0,判重就在输入边时判断该边是否已经存在,如果存在该边的入度就不在自加。
        两组比较好的测试案例:
    	第二个测试案例有5个,但是有2个一样的,所以按 4 个算
    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<cstdio>   
    #include<iostream>   
    #include<cstdlib>   
    #include<cstring>   
    #include<queue>   
    
    using namespace std;  
    
    int g[210][210];
    int degree[210];//入度   
    int value[210];
    priority_queue<int> q;//定义优先队列
    
    //得到入度为0的点   
    int toposort(int n)  
    {
        int j=n;
        for(int i=1;i<=n;i++)
        {
            if(degree[i]==0)
            {
                q.push(i);
            }
        }
        if(q.empty())
            return 0;
        while(!q.empty())
        {
            int t = q.top();
            q.pop();
            value[t]=j;  
            j--;  
            for(int i=1;i<=n;i++)  
            {  
                if(g[i][t]!=0)  
                {  
                    g[i][t]=0;  
                    degree[i]--;  
                    if(degree[i]==0)   
                        q.push(i); 
                }  
            }  
        }  
        if(j!=0)  
            return 0;  
        return 1;  
    }  
      
    int main()  
    {  
        int T;  
        int n,m;  
        int a,b;  
        scanf("%d",&T);  
        while(T--)  
        {  
            memset(g,0,sizeof(g));  
            memset(degree,0,sizeof(degree));  
            scanf("%d%d",&n,&m);  
            while(m--)  
            {  
                scanf("%d%d",&a,&b);  
                if(g[a][b]>0)		//判重,如果输入一样的那么只算一个   
                    degree[a]--;  
                g[a][b]=1;			//a到b的边,起点a,终点b的边   
                degree[a]++;		//反向建图
            }  
            int x=toposort(n);  
            if(x==0)  
                printf("-1\n");  
            else  
            {  
                for(int i=1;i<n;i++)   
                    printf("%d ",value[i]);  
                printf("%d\n",value[n]);  
            }  
        }  
        return 0;  
    }*/
    
    //代码三:---AC
    #include<iostream>
    #include<cstdlib>
    using namespace std;
    #define size 210
    int map[size][size],indegree[size],ans[size];
    int n,m;
    int main()
    {
        int i,j,k;
        int p1,p2;
        int t;
        cin>>t;
        while(t--)
        {
            cin>>n>>m;
            memset(map,0,sizeof(map));
            memset(indegree,0,sizeof(indegree));
            for(i=0;i<m;i++)
            {
                cin>>p1>>p2;
                if(!map[p2][p1])      //反向建图
                {
                    map[p2][p1]=1;
                    indegree[p1]++;
                }
            }
            for(i=n;i>=1;i--)    //i 表示重量 从高到低依次选择可以选择的最大编号
            {
                for(j=n;j>=1;j--)  //j 表示每次可能选出的(即出度为0)最大编号,那么该编号一定能够排在其他所有编号的后边,
                {                   //找出度为0的最大编号使其分配最大的重量,那么最后找出的解一定是最优解。
                    if(indegree[j]==0)
                    {
                        indegree[j]--;
                        ans[j]=i;
                        for(k=1;k<=n;k++)
                            if(map[j][k]==1)
                                indegree[k]--;
                            break;
                    }
                }
                if(j<1)
                    break;
            }
            if(i>=1)
                cout<<"-1\n";
            else
            {
                for(i=1;i<n;i++)
                    cout<<ans[i]<<" ";
                cout<<ans[n]<<endl;
            }
        }
        return 0;   
    }
    

      

    功不成,身已退
  • 相关阅读:
    asp.net core 使用 StaticFiles 中间件 (不完整翻译)
    asp.net core 通过 TeamCity 实现持续集成笔记
    Swashbuckle for asp.net core 配置说明
    # TypeScript 中如何确保 this 的正确性
    MySql + EF6 + .Net Core
    ASP.NET Core + EF6
    数据库设计 Assignment 02
    NYOJ 8 一种排序
    NYOJ 23.取石子(一)
    邻接表(C++)
  • 原文地址:https://www.cnblogs.com/dongsheng/p/2742580.html
Copyright © 2011-2022 走看看