/* 返回顶部 */

Luogu P3243 [HNOI2015]菜肴制作

传送门

这道题的正解贪心思路我看了题解....

给出点对的先后次序,很容易想到拓扑排序。

但是如果按字典序排序,就会遇到较小的就放在前面,

可以举出反例:4种菜肴,限制为<2,4><3,1>,

那么字典序最小的是2,3,1,4,但正解应为3,1,2,4.

因为按字典序排序只能按前一个点排,不能知道后一个点的大小。

通过观察发现,想让最小的尽早出现,就是要让较大的尽可能晚出现。

而可以放在后面的点是确定的。

那么反向连边,最后输出拓扑的倒序即可。

注意,不满足条件的情况除了开始就没有入度为0的点外,还有可能有部分几个点形成闭环。

所以最后要判断排出顺序的点数是否与总点数相等。

代码如下

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
#include<queue>
using namespace std;
const int maxn = 1e5+10;
int t,n,m,x,y,cnt,num;
int a[maxn],inn[maxn];
int head[maxn],to[maxn],nxt[maxn];

void add(int x,int y) {
    to[++cnt] = y;
    nxt[cnt] = head[x];
    head[x] = cnt;
}

void init() {
    cnt = num = 0;
    memset(head,0,sizeof(head));
    memset(to,0,sizeof(to));
    memset(nxt,0,sizeof(nxt));
    memset(inn,0,sizeof(inn));
}

void topo() {
    priority_queue <int> q;
    for(int i = 1; i <= n; i++)
        if(!inn[i]) q.push(i);
    if(q.empty()) {
        printf("Impossible!\n");
        return;
    }
    while(!q.empty()) {
        int u = q.top();
        q.pop();
        a[++num] = u;
        if(!head[u]) continue;
        for(int i = head[u]; i; i = nxt[i]) {
            int v = to[i];
            inn[v]--;
            if(inn[v] == 0) q.push(v);
        }
    }
    if(num < n) {
        printf("Impossible!\n");
        return;
    }
    for(int i = n; i >= 1; i--)
        printf("%d ",a[i]);
    printf("\n");
}

int main() {
    scanf("%d",&t);
    while(t--) {
        init();
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= m; i++) {
            scanf("%d%d",&x,&y);
            add(y,x);
            inn[x]++;
        }
        topo();
    }
    return 0;
}
View Code

 

posted @ 2019-05-28 02:53  Mogeko  阅读(114)  评论(0编辑  收藏  举报