把奖金更高的意见反向建边得到 DAG,用 Kahn 拓扑排序按前驱最大值递推每人最小奖金,成环则输出 Poor Xed
OJ: roj
题目 ID: 1352
难度:普及-
标签:拓扑排序图论DAGpython
创建: 2026-09-30 06:35
更新: 2026-10-07 13:50
后置题目
luogu P1347[ECNA 2001] 排序A 教的 Kahn 拓扑排序加「出队点数少于 n 即有环」的环检测,被 B 的 topological_status(len(order)<n 返回 inconsistent)原样复用;B 只是在其上叠加每加一条边重跑与队列分支数判唯一。luogu P4017最大食物链计数B 直接复用 A 教的「入度归零即所有前驱已定型、此时沿边由前驱递推」这一步,只把取 max 下界换成 ways[v]+=ways[u] 计数并补上生产者/消费者与取模。luogu P6145[USACO20FEB] Timeline GB 把 A 教的『沿拓扑序用前驱值加边权取 max 松弛后继』原样搬用,只把权重从固定的 1 换成任意 x、初值从 100 换成下界 S[i],因此 A 是 B 的模板台阶。luogu P2712摄像头B 的 main.cpp 里 TopologicalSort::kahn_prune() 直接复用 A 教的这一步 Kahn 循环——「出队遍历后继、入度减 1、归零再入队」并用删点数(A 用出队计数 done 判环,B 用 removed 得 n-removed),只是把 A 的「反向建边 + pay[v]=max(pay[u])+1 定薪递推」换成位置到摄像头的建模与剩余计数。
题目描述
公司要给 n 个员工发奖金,每人每张至少 100 元。m 条意见形如 “a 应该比 b 高”,即 Poor Xed。n≤10000,m≤20000。
输入:第一行 n m;之后 m 行每行 a b。输出:最少总奖金或 Poor Xed。
样例:2 1\n1 2 → 201(pay[2]=100,pay[1]=101)。
思路
把"a 高于 b"建成反向边 b→a,沿这条方向 Kahn 拓扑排序:入度归零的人直接取 100 元,出队时把后继 v 抬到 Poor Xed,否则累加 pay 即为最小总奖金。
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 06:35
# update_at: 2026-09-30 06:35
import sys
from collections import deque
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n, m = next(data), next(data)
# 约束 “a 比 b 高” 改写成 b -> a:沿这个方向拓扑排序,才能先定低薪再定高薪
succ: list[list[int]] = [[] for _ in range(n + 1)]
indeg = [0] * (n + 1)
for _ in range(m):
a, b = next(data), next(data)
succ[b].append(a)
indeg[a] += 1
pay = [100] * (n + 1) # 每位员工奖金最少 100 元
q = deque(i for i in range(1, n + 1) if not indeg[i])
done = 0
while q:
u = q.popleft()
done += 1
for v in succ[u]:
pay[v] = max(pay[v], pay[u] + 1) # 至少比更低的人多 1 元
indeg[v] -= 1
if not indeg[v]: # 所有更低的人都已定薪,v 才能入队定薪
q.append(v)
if done < n: # 还有人没轮到:约束成环,互相矛盾
print("Poor Xed")
else:
print(sum(pay[1:]))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 11:24
* update_at: 2026-10-05 11:24
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 10005; // 员工数 n <= 10000
const int MAXM = 20005; // 意见条数 m <= 20000
// 反向图:约束 "a 应该比 b 高" 改写成 b -> a,沿这条边方向拓扑,才能先定低薪再定高薪。
int head[MAXN], to[MAXM], nxt[MAXM], edge_cnt;
int indeg[MAXN]; // 入度:a 的入度 = 让 a 必须更高的"下级"个数
ll pay[MAXN]; // 每人最终奖金;用 ll 累加并兼容极端值
// 加一条 u -> v 的有向边(u 工资更低 / v 工资更高)。
void add_edge(int u, int v) {
edge_cnt++;
to[edge_cnt] = v;
nxt[edge_cnt] = head[u];
head[u] = edge_cnt;
}
int n, m;
void read_input() {
cin >> n >> m;
for (int i = 1; i <= n; i++) head[i] = 0, indeg[i] = 0;
edge_cnt = 0;
for (int i = 1; i <= m; i++) {
int a, b; // a 应该比 b 高 => 建边 b -> a
cin >> a >> b;
add_edge(b, a);
indeg[a]++;
}
}
void solve() {
for (int i = 1; i <= n; i++) pay[i] = 100; // 每位员工奖金最少 100 元
queue<int> q; // Kahn 队列:所有入度为 0 的点(没有更低的人要超过)
for (int i = 1; i <= n; i++) {
if (indeg[i] == 0) q.push(i);
}
int done = 0; // 已经定薪出队的员工数
while (!q.empty()) {
int u = q.front(); q.pop();
done++;
// u 已经定薪,把后继端点 v 抬到 max(pay[v], pay[u] + 1)
for (int e = head[u]; e != 0; e = nxt[e]) {
int v = to[e];
if (pay[u] + 1 > pay[v]) pay[v] = pay[u] + 1;
indeg[v]--;
if (indeg[v] == 0) q.push(v);
}
}
if (done < n) { // 还有人没轮到:约束成环,互相矛盾,无解
cout << "Poor Xed";
}
else {
ll total = 0;
for (int i = 1; i <= n; i++) total += pay[i];
cout << total;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
read_input();
solve();
return 0;
}