1. 问题描述:
由于无敌的凡凡在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
输入样例:
2 1
1 2
输出样例:
201
来源:https://www.acwing.com/problem/content/description/1194/
2. 思路分析:
分析题目可以知道这是一道差分约束简化版本的题目(只存在一种大于不等式关系),我们可以将每一个员工看成是一个节点,员工之间的奖金约束看成是一条边,并且每一条边的权重都为1,对于差分约束并且边权大于0的题目我们可以尝试使用拓扑排序来求解,如果图中存在环那么肯定无解,如果存在拓扑序说明有解,而且题目中求解的是最小值,而对于差分约束的题目来说应该求解单源最长路径,所以当我们求解出拓扑序列之后那么按照拓扑序列求解单源最长路径即可,因为每个员工的奖金至少为100,所以可以建立一个虚拟源点,源点向其余点连一条权重为100的边,但是在实际写的时候将距离初始化为100求解单源最长路径即可。
3. 代码如下:
import collections
from typing import List
class Solution:
# 拓扑排序
def topsort(self, n: int, d: List[int], g: List[List[int]], res: List[int]):
q = collections.deque()
for i in range(1, n + 1):
# 将所有入度为0的点入队
if d[i] == 0:
q.append(i)
res.append(i)
while q:
p = q.popleft()
for next in g[p]:
d[next] -= 1
if d[next] == 0:
q.append(next)
res.append(next)
# 判断拓扑序列的长度是否等于n
if len(res) == n: return 1
return -1
def process(self):
n, m = map(int, input().split())
# 员工的编号从1开始
g = [list() for i in range(n + 10)]
d = [0] * (n + 10)
for i in range(m):
a, b = map(int, input().split())
# 因为a >= b + 1所以对于差分约束的问题应该是b向a连一条权重为1的有向边, 权重其实不用存下来
# b向a连一条有向边
g[b].append(a)
d[a] += 1
res = list()
t = self.topsort(n, d, g, res)
if t == -1:
print("Poor Xed")
else:
# 初始化每一个节点都为100, 表示每一个员工的奖金至少为100元, 相当于是每一个节点向其余点连一条长度为100的边
dis = [100] * (n + 10)
# 按照拓扑序序列求解最短路径
for i in range(n):
for next in g[res[i]]:
# 每一条边的权重为1
dis[next] = max(dis[next], dis[res[i]] + 1)
res = 0
# 求解所有员工至少需要的奖金
for i in range(1, n + 1):
res += dis[i]
print(res)
if __name__ == "__main__":
Solution().process()