POJ3687,拓扑排序

这题首先要明确输出什么——–输出从1-N编号的球的重量,并且尽可能使1号球轻,如果1号轻一样重,则输出2号球尽可能轻的方案,…以此类推.
如果按照正常的思路,就是拓扑排序,每次取出最小的编号,使其重量为当前最小。但是这样存在一个问题,就是:当前最小的编号的球不一定是能够最先接近1球,有可能当前编号最大的那个或者其他球最先接近1号球。所以正向就不能够得到正确的答案。可以这样想:每次我都将编号最大的、离1号球最远的那个球,使其重量为当前重量的最大值,那么1号球最终的剩下的选择肯定就是最轻的了!
因此我们可以这样做:如何使远离1号或者小编号的大编号球重量取得最大值呢?用优先队列,每次取出最大的编号,使其重量为最大值(这样保证了小编号的球重量比较轻)。同时反向建图,因为输入中的右边是比较重的球,由于我们是要先给重的球赋重量,所以就要反向建图。
代码如下:

/*************************************************************************
    > File Name: main.cpp
    > Author:Eagles 
    > Mail:None 
    > Created Time: 2018年09月16日 星期日 10时27分28秒
    > Description:POJ3687,拓扑排序 
 ************************************************************************/

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define N 1000
struct node
{
    int to;
    int nex;
}E[N*N];
int head[N];
int in_deg[N];
priority_queue<int, vector<int>, less<int> >q;

int cnt,num,n,m;
int ans[N];
bool flag;

bool addEdge(int a, int b)
{
    for (int i=head[a]; i!=-1; i=E[i].nex)
    {
        if (E[i].to==b)
            return false;
    }
    E[cnt].to=b;
    E[cnt].nex=head[a];
    head[a]=cnt++;
    return true;
}

void init()
{
    memset(head,-1,sizeof(head));
    memset(in_deg,0,sizeof(in_deg));
    num=cnt=0;
    flag=true;
    while (!q.empty())
        q.pop();

    scanf("%d%d",&n,&m);

    for (int i=0; i<m; i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if (a==b)
            flag=false;
        if (addEdge(b,a))
            in_deg[a]++;
    }
}

void toposort()
{
    for (int i=n ;i>0; i--)
    {
        if (in_deg[i]==0)
            q.push(i);
    }

    int sum=n; 

    while (!q.empty())
    {
        int t=q.top();
        q.pop();

        ans[t]=sum--;

        for (int i=head[t]; i!=-1; i=E[i].nex)
        {
            in_deg[E[i].to]--;

            if (in_deg[E[i].to]==0)
                q.push(E[i].to);
        }
    }

    if (sum>0)
        flag=false;
}

void get_ans()
{
    if (flag)
    {
        for (int i=1; i<=n; i++)
        {
            printf("%d ",ans[i]);
        }
        printf("\n");
    }
    else
        printf("-1\n");
}

void print()
{
    printf("\n");
    for (int i=1; i<=n; i++)
    {
        for (int j=head[i]; j!=-1; j=E[j].nex)
        {
            printf("%d %d\n",i,E[j].to);
        }
    }
}
int main()
{
    int t;

    while (~scanf("%d",&t))
    {
        while (t--)
        {
            init();
            toposort();
            get_ans();
        }
    }
    return 0;
}

所以,在这里,优先队列的作用是:取得大编号球;反向建图的作用是:使重的球进入队列。二者合作,即可取得大编号的重球,剩下的自然就是小编号的轻球啦!

发布了45 篇原创文章 · 获赞 2 · 访问量 3086

猜你喜欢

转载自blog.csdn.net/wysiwygo/article/details/82724051