欢迎光临
我们一直在努力

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

本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。

欢迎大家订阅我的专栏:算法题解:C++与Python实现!

附上汇总贴:算法竞赛备考冲刺必刷题(C++) | 汇总


【题目来源】

洛谷: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

    n

    n 个人及其初始姓名,每个人有准备送礼的钱数

    m

    o

    n

    e

    y

    money

    money 和送礼对象数量

    n

    u

    m

    num

    num。每个人将

    m

    o

    n

    e

    y

    money

    money 元平均分给

    n

    u

    m

    num

    num 个送礼对象,每人得到

    m

    o

    n

    e

    y

    /

    n

    u

    m

    \\lfloor money / num \\rfloor

    money/num 元,余数

    m

    o

    n

    e

    y

    m

    o

    d

    n

    u

    m

    money \\bmod num

    moneymodnum 返还给送礼者。需要计算每个人收到的钱减去送出的钱的净值,按输入姓名顺序输出。

  • 算法选择:

    • 结构体模拟:用结构体数组维护每个人的姓名、收入 in 和支出 out
    • 哈希映射(线性查找):通过姓名匹配找到对应的人,更新其收支记录
    • 整数除法取整:利用整数除法自动实现

      m

      o

      n

      e

      y

      /

      n

      u

      m

      \\lfloor money / num \\rfloor

      money/num,取模实现余数返还

  • 关键步骤:

    • 初始化:读取

      n

      n

      n,依次录入

      n

      n

      n 个人的姓名,初始化 in = 0,out = 0

    • 处理每个送礼者:
      • 读取送礼者姓名

        a

        a

        a、准备金额

        m

        o

        n

        e

        y

        money

        money、送礼人数

        n

        u

        m

        num

        num

      • n

        u

        m

        =

        0

        num = 0

        num=0,跳过(无人可送,钱全部保留,无需操作)

      • 找到姓名

        a

        a

        a 对应的人:

        • out -= money:标记该人支出了

          m

          o

          n

          e

          y

          money

          money

        • in += money % num:将除不尽的余数加回收入
      • 依次读取

        n

        u

        m

        num

        num 个接收者姓名

        b

        b

        b,找到对应的人:

        • in += money / num:每个接收者增加

          m

          o

          n

          e

          y

          /

          n

          u

          m

          \\lfloor money / num \\rfloor

          money/num 元收入

    • 输出结果:
      • 遍历

        n

        n

        n 个人,输出 name 和 in + out(即净收益,

        o

        u

        t

        out

        out 为负数表示支出)

  • 时间/空间复杂度:

    • 时间复杂度:

      O

      (

      n

      (

      n

      +

      总送礼人数

      )

      )

      O(n \\cdot (n + \\text{总送礼人数}))

      O(n(n+总送礼人数)),外层遍历

      n

      n

      n 个送礼者,内层每次线性查找

      O

      (

      n

      )

      O(n)

      O(n),总送礼人数不超过

      n

      2

      n^2

      n2

    • 空间复杂度:

      O

      (

      n

      )

      O(n)

      O(n),结构体数组存储

      n

      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

    赞(0)
    未经允许不得转载:171主机测评 » 题解:洛谷 P1201 [USACO1.1] 贪婪的送礼者 Greedy Gift Givers
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址