codeforces 825 E Minimal Labels

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/HackerTom/article/details/75226508

Problem

codeforces.com/contest/825/problem/E

Reference

看第 5 条评论

Meaning

给出一个n个结点的DAG,找一个给结点编号的序列,且满足3个条件:

  • 编号为 1~n,每个编号出现且仅出现一次;
  • 如果存在边从 f 指向 t,则结点 f 的编号要小于 t 的编号;
  • 在所有可行的编号序列中,取字典序最小的;

Analysis

第二个条件就像拓扑排序,再要满足足第三个条件,就在拓扑排序时,每次从所有入度为零的点中,找出最小的点并给它当前可用的最小的编号。
然而,这样做是错的。
比如样例:

---Input---
6 5
6 2
5 2
2 1
4 3
3 1
---Outpuut---
6 3 5 4 1 2

之前像上面那样写(有点像贪心),结果是:6 5 2 1 3 4
可能要先给某些大的点分配小的编号来“解锁”小的点,来取得更小的字典序。
按评论里给出的题解,是要反过来想,存反向边,每次也是考虑入度为零的点,但是选最大的点分配最大的编号。
评论里的证明原文(by krijgertje):

We can prove it by contradiction. Take any graph with the smallest number of nodes for which this algorithm does not give an optimal labeling. The largest label must always be assigned to a node with no outgoing edges, but it can’t be the largest of those nodes (otherwise the algorithm would give the optimal labeling). Lets call the largest node with no outgoing edges x and the node that has the largest label in the optimal labeling y. Then after labeling y with the largest label, the remaining part of the graph is correctly labeled by the algorithm (otherwise we would have a smaller graph for which the algorithm gives an incorrect result). This will first label some nodes greater than x and then it will label x. But we get a better labeling by labeling x with the largest label, y with the next largest and then label the same nodes as before. This is because of the nodes labeled so far y is the smallest node (since y < x and all other labelled nodes are greater than x) and in the partial labeling we have labeled the same nodes using the same labels. So we have a contradiction, which means our assumption was wrong and therefore there is no graph that our algorithm labels incorrectly.

Accepted Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int N = 100000, M = 100000;

vector<int> rev[N+1]; // reverse edge
int deg[N+1]; // degree of vertex
int label[N+1]; // label sequence
priority_queue<int> que;

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    memset(deg, 0, sizeof deg);
    for(int f, t, i=0; i<m; ++i)
    {
        scanf("%d%d", &f, &t);
        rev[t].push_back(f);
        ++deg[f];
    }
    for(int i=1; i<=n; ++i)
        if(!deg[i])
            que.push(i);
    for(int lab=n, t; lab > 0; --lab) // desc
    {
        t = que.top();
        que.pop();
        label[t] = lab;
        for(int i=0; i<rev[t].size(); ++i)
            if(--deg[rev[t][i]] == 0)
                que.push(rev[t][i]);
    }
    for(int i=1; i<=n; ++i)
        printf("%d%c", label[i], i==n?'\n':' ');
    return 0;
}

Wrong Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int N = 100000, M = 100000;

vector<int> g[N+1]; // edge
int deg[N+1];
int label[N+1];
priority_queue<int, vector<int>, greater<int> > que;

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    memset(deg, 0, sizeof deg);
    for(int f, t, i=0; i<m; ++i)
    {
        scanf("%d%d", &f, &t);
        g[f].push_back(t);
        ++deg[t];
    }
    for(int i=1; i<=n; ++i)
        if(!deg[i])
            que.push(i);
    for(int lab=1, t; lab <= n; ++lab) // ascend
    {
        t = que.top();
        que.pop();
        label[t] = lab;
        for(int i=0; i<g[t].size(); ++i)
            if(--deg[g[t][i]] == 0)
                que.push(g[t][i]);
    }
    for(int i=1; i<=n; ++i)
        printf("%d%c", label[i], i==n?'\n':' ');
    return 0;
}
展开阅读全文

没有更多推荐了,返回首页