【题目描述】
由于无敌的凡凡在2005年世界英俊帅气男总决选中胜出,Yali Company总经理Mr.Z心情好,决定给每位员工发奖金。公司决定以每个人本年在公司的贡献为标准来计算他们得到奖金的多少。
于是Mr.Z下令召开m方会谈。每位参加会谈的代表提出了自己的意见:“我认为员工a的奖金应该比b高!”Mr.Z决定要找出一种奖金方案,满足各位代表的意见,且同时使得总奖金数最少。每位员工奖金最少为100元。
【输入】
第一行两个整数n,m,表示员工总数和代表数;
以下m行,每行2个整数a,b,表示某个代表认为第a号员工奖金应该比第b号员工高。
【输出】
若无法找到合理方案,则输出“Poor Xed
”;否则输出一个数表示最少总奖金。
【输入样例】
2 1
1 2
【输出样例】
201
【提示】
【数据规模】
80%的数据满足:n≤1000,m≤2000;
100%的数据满足:n≤10000,m≤20000。
解题思路:
这道题目是一个典型的拓扑排序问题。我们可以按照如下的步骤来解决这个问题:
- 建图:首先,需要根据输入的代表意见建立一个有向图。在这个图中,如果某个代表认为员工a的奖金应该比员工b高,那么我们就从b向a连一条有向边。
- 拓扑排序:接下来,需要对这个有向图进行拓扑排序。拓扑排序的目的是为了确定员工之间奖金高低的相对顺序。如果图中存在环,那么就说明存在矛盾的意见,即无法找到一个合理的奖金方案满足所有代表的要求。在这种情况下,我们应该输出“Poor Xed”。
拓扑排序实现过程:每次先查找入度为0的点,放入队列,然后取出它指向的那些点,把他们的工资+=目前入度为0的点的工资-100(增值);然后将入度为0的点删除,对应的邻接点入度减1;循环,不断查找入度为0的点,直到找不到为止。
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 10005; // 假设员工数量的最大值为10000
vector<int> graph[MAXN]; // 邻接表存储图
int inDegree[MAXN]; // 存储每个员工的入度
int bonus[MAXN]; // 存储每个员工的奖金数
bool visited[MAXN]; // 标记员工是否已访问过
// 拓扑排序,并返回是否成功分配了奖金
bool topsort(int n, long long &totalBonus) {
queue<int> q;
totalBonus = 0;
// 初始化visited和bonus数组
for (int i = 1; i <= n; i++) {
visited[i] = false;
bonus[i] = 1; // 初始奖金至少为1
}
// 将所有入度为0的员工加入队列,并给他们分配初始奖金100
for (int i = 1; i <= n; i++) {
if (inDegree[i] == 0) {
q.push(i);
bonus[i] = 100; // 可以根据题目要求调整初始奖金
}
}
// 拓扑排序
while (!q.empty()) {
int u = q.front();
q.pop();
visited[u] = true;
totalBonus += bonus[u]; // 累加总奖金
// 遍历u的所有邻居v
for (int i = 0; i < graph[u].size(); i++) {
int v = graph[u][i];
if (!visited[v]) {
// 更新v的奖金,确保它比u的奖金多
bonus[v] = max(bonus[v], bonus[u] + 1);
// 如果v的入度减为0,则将其加入队列
if (--inDegree[v] == 0) {
q.push(v);
}
}
}
}
// 检查是否所有员工都被访问过,如果有环则无法分配奖金
for (int i = 1; i <= n; i++) {
if (!visited[i]) {
return false; // 存在环,无法分配奖金
}
}
return true; // 成功分配奖金
}
int main() {
int n, m; // n为员工数量,m为意见数量
cin >> n >> m;
// 读取意见并构建图
for (int i = 0; i < m; i++) {
int a, b; // a的奖金应该比b高
cin >> a >> b;
graph[b].push_back(a); // 添加边b->a,表示a的奖金应该比b高(注意方向)
inDegree[a]++; // a的入度加1
}
long long totalBonus = 0; // 总奖金数,可能很大,所以用long long
if (topsort(n, totalBonus)) { // 如果能成功分配奖金
cout << totalBonus << endl; // 输出最少总奖金数
} else {
cout << "Poor Xed" << endl; // 无法分配奖金的情况
}
return 0;
}
有五个关键点需要注意:
- 边的方向:
- 确保了边的方向是正确的。如果员工A的奖金必须高于员工B,那么在图中应该有一条从B指向A的边。这是因为拓扑排序是基于有向无环图(DAG)的,边的方向很重要。
- 入度的处理:
- 使用了一个数组
inDegree
来跟踪每个员工的入度(即指向该员工的边的数量)。这是拓扑排序所必需的,因为它决定了哪些节点可以首先被访问。
- 使用了一个数组
- 奖金的分配和更新:
- 使用了一个数组
bonus
来存储每个员工的奖金数。初始时,所有员工的奖金都设置为100。然后,在拓扑排序的过程中,我确保每当访问一个员工时,都会更新其邻居的奖金,以确保它们比当前员工的奖金高。
- 使用了一个数组
- 拓扑排序的结束条件和环的检测:
- 代码中,拓扑排序结束后,检查了一个
visited
数组来确保所有员工都被访问过。如果有任何员工没有被访问到,那么这意味着图中存在环,因此无法分配奖金。
- 代码中,拓扑排序结束后,检查了一个
- 总奖金的计算:
- 使用一个
long long
类型的变量totalBonus
来累加计算总奖金。这是为了确保即使员工数量很多时,总奖金也不会溢出。
- 使用一个