You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##AcWing 1192. 奖金

### 一、题目描述 由于无敌的凡凡在2005年世界英俊帅气男总决选中胜出,Yali Company总经理Mr.Z心情好,决定给每位员工发奖金。

公司决定以每个人本年在公司的贡献为标准来计算他们得到奖金的多少。

于是Mr.Z下令召开 m 方会谈。

每位参加会谈的代表提出了自己的意见:“我认为员工 a 的奖金应该比 b 高!”

Mr.Z决定要找出一种奖金方案,满足各位代表的意见,且同时 使得总奖金数最少

每位员工奖金最少为100元,且必须是整数。

输入格式 第一行包含整数 n,m,分别表示公司内员工数以及参会代表数。

接下来 m 行,每行 2 个整数 a,b,表示某个代表认为第 a 号员工奖金应该比第 b 号员工高。

输出格式 若无法找到合理方案,则输出“Poor Xed”;

否则输出一个数表示 最少总奖金

数据范围 1≤n≤10000,1≤m≤20000,1≤a,b≤n

输入样例

2 1
1 2

输出样例

201

二、问题与解答

Q1:本题与拓扑序有什么关系?

如果a>b,b>c,a>c,那么这个图是有向无环图DAG \Leftrightarrow 存在拓扑序 如果a>b,b>c,c>a,那么这个图是有向有环图   \Leftrightarrow 不存在拓扑序 所以,可以利用bfs+维护入度的办法,检查此图,是否能够构成DAG,是则求DAG路径,不是表示有环,在本题中就是存在逻辑上的冲突,无解。

Q2:如何求DAG路径+每个点的最小值?

  • 在用bfs+维护入度方法检查图时,就可以一同维护一下点的顺序,也就是DAG路径。
  • 每个点的最小值,其实是受所有来源点的要求决定,比如a要求b101c要求b102,那么b只能是102,此时,看似取的是多个来源的最大值,其实这才是此点b的最小值。

三、拓扑排序+dp最长路

思路:利用拓扑序判环,利用拓扑序找出的点进行三角不等式计算,找出最大值,累加最大值。

注意:添加超级源点是一个经典办法。

#include <bits/stdc++.h>
using namespace std;
const int N = 10010, M = 30010;

int din[N];  // 记录每个节点入度
int dist[N]; // 记录每个节点距离起点的最小值
int n, m;
// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

vector<int> path;
bool topsort() {
    queue<int> q;
    for (int i = 1; i <= n; i++)
        if (!din[i]) q.push(i);

    while (q.size()) {
        int u = q.front();
        q.pop();
        path.push_back(u); // 记录拓扑序路径
        for (int i = h[u]; ~i; i = ne[i]) {
            int v = e[i];
            din[v]--;
            if (din[v] == 0) q.push(v);
        }
    }
    return path.size() == n;
}

int main() {
    memset(h, -1, sizeof h);
    scanf("%d %d", &n, &m);
    while (m--) {
        int a, b;
        scanf("%d %d", &a, &b);
        add(b, a, 1); // a比b高意味着 b->边权为1的边->a
        din[a]++;
    }

    if (!topsort()) {
        puts("Poor Xed");
        return 0;
    }

    // 每个员工奖金最少是100元
    for (int i = 1; i <= n; i++) dist[i] = 100;

    // 根据拓扑序求最长路
    for (auto u : path) { // 枚举拓扑序路径
        // 枚举节点u所有邻接的节点找出最大的转移,可以看一下题解中的图,就明白了
        for (int i = h[u]; ~i; i = ne[i]) {
            int v = e[i];
            dist[v] = max(dist[v], dist[u] + w[i]);
        }
    }
    int res = 0;
    for (int i = 1; i <= n; i++) res += dist[i]; // 每个点的累加和,就是总的,最小的, 奖金数
    printf("%d\n", res);

    return 0;
}