信息学奥赛一本通题目解析:1352:【例4-13】奖金

【题目描述】

由于无敌的凡凡在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。

解题思路:

这道题目是一个典型的拓扑排序问题。我们可以按照如下的步骤来解决这个问题:

  1. 建图:首先,需要根据输入的代表意见建立一个有向图。在这个图中,如果某个代表认为员工a的奖金应该比员工b高,那么我们就从b向a连一条有向边。
  2. 拓扑排序:接下来,需要对这个有向图进行拓扑排序。拓扑排序的目的是为了确定员工之间奖金高低的相对顺序。如果图中存在环,那么就说明存在矛盾的意见,即无法找到一个合理的奖金方案满足所有代表的要求。在这种情况下,我们应该输出“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;  
}

有五个关键点需要注意:

  1. 边的方向
    • 确保了边的方向是正确的。如果员工A的奖金必须高于员工B,那么在图中应该有一条从B指向A的边。这是因为拓扑排序是基于有向无环图(DAG)的,边的方向很重要。
  2. 入度的处理
    • 使用了一个数组inDegree来跟踪每个员工的入度(即指向该员工的边的数量)。这是拓扑排序所必需的,因为它决定了哪些节点可以首先被访问。
  3. 奖金的分配和更新
    • 使用了一个数组bonus来存储每个员工的奖金数。初始时,所有员工的奖金都设置为100。然后,在拓扑排序的过程中,我确保每当访问一个员工时,都会更新其邻居的奖金,以确保它们比当前员工的奖金高。
  4. 拓扑排序的结束条件和环的检测
    • 代码中,拓扑排序结束后,检查了一个visited数组来确保所有员工都被访问过。如果有任何员工没有被访问到,那么这意味着图中存在环,因此无法分配奖金。
  5. 总奖金的计算
    • 使用一个long long类型的变量totalBonus来累加计算总奖金。这是为了确保即使员工数量很多时,总奖金也不会溢出。
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
信息学奥赛一本通1255:迷宫问题是一个关于迷宫的问题。这个问题要求通过广搜算法来解决迷宫问题,找到走出迷宫的路径。具体来说,迷宫可以看成是由n×n的格点组成,每个格点只有两种状态, "." 和 "#" 。其中 "." 代表可通行的路径,"#" 代表不可通行的墙壁。通过广搜算法,我们可以搜索从起点到终点的路径,找到一条合法的路径即可。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [信息学奥赛一本通 1255:迷宫问题 | OpenJudge NOI 2.5 7084:迷宫问题](https://blog.csdn.net/lq1990717/article/details/124721407)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [c++信息学奥赛一本通1215题解](https://download.csdn.net/download/Asad_Yuen/87357807)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [信息学奥赛一本通(1255:迷宫问题)](https://blog.csdn.net/lvcheng0309/article/details/118879231)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值