题解:洛谷 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
【核心思想】
-
问题分析:给定 \(n\) 个人及其初始姓名,每个人有准备送礼的钱数 \(money\) 和送礼对象数量 \(num\)。每个人将 \(money\) 元平均分给 \(num\) 个送礼对象,每人得到 \(\lfloor money / num \rfloor\) 元,余数 \(money \bmod num\) 返还给送礼者。需要计算每个人收到的钱减去送出的钱的净值,按输入姓名顺序输出。
-
算法选择:
- 结构体模拟:用结构体数组维护每个人的姓名、收入
in和支出out - 哈希映射(线性查找):通过姓名匹配找到对应的人,更新其收支记录
- 整数除法取整:利用整数除法自动实现 \(\lfloor money / num \rfloor\),取模实现余数返还
- 结构体模拟:用结构体数组维护每个人的姓名、收入
-
关键步骤:
- 初始化:读取 \(n\),依次录入 \(n\) 个人的姓名,初始化
in = 0,out = 0 - 处理每个送礼者:
- 读取送礼者姓名 \(a\)、准备金额 \(money\)、送礼人数 \(num\)
- 若 \(num = 0\),跳过(无人可送,钱全部保留,无需操作)
- 找到姓名 \(a\) 对应的人:
out -= money:标记该人支出了 \(money\) 元in += money % num:将除不尽的余数加回收入
- 依次读取 \(num\) 个接收者姓名 \(b\),找到对应的人:
in += money / num:每个接收者增加 \(\lfloor money / num \rfloor\) 元收入
- 输出结果:
- 遍历 \(n\) 个人,输出
name和in + out(即净收益,\(out\) 为负数表示支出)
- 遍历 \(n\) 个人,输出
- 初始化:读取 \(n\),依次录入 \(n\) 个人的姓名,初始化
-
时间/空间复杂度:
- 时间复杂度:\(O(n \cdot (n + \text{总送礼人数}))\),外层遍历 \(n\) 个送礼者,内层每次线性查找 \(O(n)\),总送礼人数不超过 \(n^2\)
- 空间复杂度:\(O(n)\),结构体数组存储 \(n\) 个人的信息
-
模拟的核心思想:
- 直接映射现实流程:按照题目描述的送礼过程逐步模拟,不追求数学公式化简
- 收支分离记录:用
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
浙公网安备 33010602011771号