题解:洛谷 P1201 贪婪的送礼者

【题目来源】

洛谷:P1201 [USACO1.1] 贪婪的送礼者 Greedy Gift Givers - 洛谷

【题目描述】

对于一群 n 个要互送礼物的朋友,GY 要确定每个人送出的钱比收到的多多少。在这一个问题中,每个人都准备了一些钱来送礼物,而这些钱将会被平均分给那些将收到他的礼物的人。

然而,在任何一群朋友中,有些人将送出较多的礼物(可能是因为有较多的朋友),有些人有准备了较多的钱。

给出一群朋友,没有人的名字会长于 14 字符,给出每个人将花在送礼上的钱,和将收到他的礼物的人的列表,请确定每个人收到的比送出的钱多的数目。

【输入】

第一行一个正整数 n,表示人数。接下来 n 行,每行一个字符串表示人名。

接下来有 n 段内容,对于每一段第一行是将会送出礼物人的名字。第二行包含二个非负整数,第一个是原有的钱的数目( ∈[0,2000] ),第二个 gi 是将收到这个人礼物的人的个数 如果 gi≠ 0,在下面 gi 行列出礼物的接受者的名字,一个名字一行。

【输出】

输出共 n 行,每行输出一个人的名字和该人收到的钱比送出的钱多的数目。名字的顺序应该与输入第 2 行至 n+1 行的顺序相同。

送出的钱永远是整数,即假设送礼人一次向 m 人送出 n 元,每个人应该得到 ⌊n/m⌋ 元。剩余未送出的钱应返还给送礼者。

【输入样例】

5
dave
laura
owen
vick
amr
dave
200 3
laura
owen
vick
owen
500 1
dave
amr
150 2
vick
owen
laura
0 2
amr
vick
vick
0 0

【输出样例】

dave 302
laura 66
owen -359
vick 141
amr -150

【核心思想】

  1. 问题分析:给定 \(n\) 个人及其初始姓名,每个人有准备送礼的钱数 \(money\) 和送礼对象数量 \(num\)。每个人将 \(money\) 元平均分给 \(num\) 个送礼对象,每人得到 \(\lfloor money / num \rfloor\) 元,余数 \(money \bmod num\) 返还给送礼者。需要计算每个人收到的钱减去送出的钱的净值,按输入姓名顺序输出。

  2. 算法选择

    • 结构体模拟:用结构体数组维护每个人的姓名、收入 in 和支出 out
    • 哈希映射(线性查找):通过姓名匹配找到对应的人,更新其收支记录
    • 整数除法取整:利用整数除法自动实现 \(\lfloor money / num \rfloor\),取模实现余数返还
  3. 关键步骤

    • 初始化:读取 \(n\),依次录入 \(n\) 个人的姓名,初始化 in = 0out = 0
    • 处理每个送礼者
      • 读取送礼者姓名 \(a\)、准备金额 \(money\)、送礼人数 \(num\)
      • \(num = 0\),跳过(无人可送,钱全部保留,无需操作)
      • 找到姓名 \(a\) 对应的人:
        • out -= money:标记该人支出了 \(money\)
        • in += money % num:将除不尽的余数加回收入
      • 依次读取 \(num\) 个接收者姓名 \(b\),找到对应的人:
        • in += money / num:每个接收者增加 \(\lfloor money / num \rfloor\) 元收入
    • 输出结果
      • 遍历 \(n\) 个人,输出 namein + out(即净收益,\(out\) 为负数表示支出)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \cdot (n + \text{总送礼人数}))\),外层遍历 \(n\) 个送礼者,内层每次线性查找 \(O(n)\),总送礼人数不超过 \(n^2\)
    • 空间复杂度:\(O(n)\),结构体数组存储 \(n\) 个人的信息
  5. 模拟的核心思想

    • 直接映射现实流程:按照题目描述的送礼过程逐步模拟,不追求数学公式化简
    • 收支分离记录:用 in(正数累加收入)和 out(负数累加支出)分别记录,最终求和得净值
    • 整数除法的取整特性:C++ 中 money / num 自动向下取整,money % num 得到余数,恰好符合题意
    • 余数返还机制:不能整除时余数归送礼者,通过 in += money % num 实现
    • 适用于规则明确、流程清晰的统计类问题,无需复杂算法,重在准确建模

【解题思路】

【算法标签】

入门 #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, money, num;
string a, b;
struct person{  // 定义结构体
    string name;  //姓名
    int in, out;  // 收到的钱和送出的钱
}p[15];  // 定义结构体数组,要设置为15,设置为10会报RE错误
int main()
{
    cin >> n;  // 输入n
    for (int i=1; i<=n; i++) {  // 依次记录n个人的信息
        cin >> p[i].name;  // 每个人的姓名
        p[i].in = 0;  // 收到的钱
        p[i].out = 0;  // 送出的钱
    }
    while (cin >> a){  // 只要还有输入(按Ctrl+Z结束输入)
        cin >> money >> num;  // 输入2个数字,送出的钱,以及将收到这个礼物的人数
        if(num==0) continue;  // 特判人数为0时,继续循环
        for (int i=1; i<=n; i++) {  // 先遍历n个人,找到姓名为a的人
            if (a==p[i].name) {
                p[i].out = p[i].out - money;  // 记录a送出的钱
                p[i].in = p[i].in + money%num;  // 以及收到的钱,如果不能整除,则收到余数
            }
        }
        for (int i=1; i<=num; i++) {  // 再循环num次
            cin >> b;  // 记录b这个姓名
            for (int j=1; j<=n; j++) {  // 遍历n个人,找到姓名为b的人
                if (b==p[j].name) {
                    p[j].in += money / num;  // 修改这个人收到的钱
                }
            }
        }
    }
    for (int i=1; i<=n; i++) {  // 遍历n个人,依次打印姓名、收到比送出多的钱
        cout << p[i].name << " " << p[i].in+p[i].out << endl;
    }
    return 0;
}

【运行结果】

5
dave
laura
owen
vick
amr
dave
200 3
laura
owen
vick
owen
500 1
dave
amr
150 2
vick
owen
laura
0 2
amr
vick
vick
0 0
^Z
dave 302
laura 66
owen -359
vick 141
amr -150
posted @ 2026-08-02 10:55  团爸讲算法  阅读(2)  评论(0)    收藏  举报