题解:洛谷 B3872 巧夺大奖

【题目来源】

洛谷:B3872 [GESP202309 五级] 巧夺大奖 - 洛谷

【题目描述】

小明参加了一个巧夺大奖的游戏节目。主持人宣布了游戏规则:

  1. 游戏分为 \(n\) 个时间段,参加者每个时间段可以选择一个小游戏。
  2. 游戏中共有 \(n\) 个小游戏可供选择。
  3. 每个小游戏有规定的时限和奖励。对于第 \(i\) 个小游戏,参加者必须在第 \(T_i\) 个时间段结束前完成才能得到奖励 \(R_i\)

小明发现,这些小游戏都很简单,不管选择哪个小游戏,他都能在一个时间段内完成。关键问题在于,如何安排每个时间段分别选择哪个小游戏,才能使得总奖励最高?

【输入】

输入第一行,包含一个正整数 \(n\)\(n\) 既是游戏时间段的个数,也是小游戏的个数。约定 \(1\le n\le500\)

输入第二行,包含 \(n\) 个正整数。第 \(i\) 个正整数为 \(T_i\),即第 \(i\) 个小游戏的完成期限。约定 \(1\le T_i\le n\)

输入第三行,包含 \(n\) 个正整数。第 \(i\) 个正整数为 \(R_i\),即第 \(i\) 个小游戏的完成奖励。约定 \(1\le R_i\le 1000\)

【输出】

输出一行,包含一个正整数 \(C\),为最高可获得的奖励。

【输入样例】

7
4 2 4 3 1 4 6
70 60 50 40 30 20 10

【输出样例】

230

【核心思想】

  1. 问题分析:给定 \(n\) 个任务,每个任务有截止时间 \(T_i\) 和奖励 \(R_i\)。每个时间段只能完成一个任务,任务必须在截止时间前完成。求最大总奖励。这是一个经典贪心调度问题,与"带截止时间的作业调度"完全相同,关键在于按奖励降序排序,每个任务尽量安排在截止时间附近。

  2. 算法选择

    • 贪心排序:按奖励 \(R_i\) 降序排序,优先安排高奖励任务
    • 时间槽分配:对每个任务,从其截止时间 \(T_i\) 向前寻找第一个空闲时间段,安排该任务
  3. 关键步骤

    • 读入数据\(n\),截止时间数组 \(T[1..n]\),奖励数组 \(R[1..n]\)
    • 结构体排序:按 \(R_i\) 降序排序
    • 贪心安排\(i\)\(1\)\(n\)):
      • \(j = T_i\)\(1\) 向前找第一个空闲时间点
      • 若找到:ans += R_ist[j] = truebreak
      • 若未找到:该任务无法安排(跳过)
    • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n^2)\),排序 \(O(n \log n)\),每个任务最坏向前扫描 \(n\) 个时间点
    • 空间复杂度:\(O(n)\),任务数组和时间标记数组
  5. 贪心调度 + 时间槽分配的核心思想

    • 奖励优先策略:高奖励任务优先安排,因为放弃高奖励任务的代价更大
    • 截止时间附近的灵活性:从截止时间向前找空闲槽,尽量把前面的时间留给截止时间更早的任务,这是"最晚可能开始"策略
    • 交换论证:若最优解中存在低奖励任务占用了高奖励任务可安排的时间槽,交换两者不会减少总奖励
    • 时间槽的稀缺性:每个时间段只能安排一个任务,因此需要合理分配。从后向前找空闲槽,相当于"尽量不影响其他任务"
    • 适用于"带截止时间的单资源调度"经典问题,核心在于贪心排序和时间槽的最优分配

【算法标签】

普及 #贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 505;  // 最大任务数

// 任务结构体
struct Node
{
    int r;  // 任务收益
    int t;  // 任务截止时间
} a[N];

int n;    // 任务数量
int ans;  // 最大总收益
bool st[N];  // 标记时间点是否被占用

// 比较函数:按收益降序排序
bool cmp(Node x, Node y)
{
    return x.r > y.r;  // 收益高的排前面
}

int main()
{
    // 输入任务数量
    cin >> n;

    // 输入每个任务的截止时间
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i].t;
    }

    // 输入每个任务的收益
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i].r;
    }

    // 按收益降序排序
    sort(a + 1, a + n + 1, cmp);

    // 贪心算法:为每个任务安排时间
    for (int i = 1; i <= n; i++)  // 按收益从高到低处理任务
    {
        // 从任务的截止时间向前寻找空闲时间点
        for (int j = a[i].t; j >= 1; j--)
        {
            if (!st[j])  // 如果时间点j空闲
            {
                ans += a[i].r;  // 安排任务,累加收益
                st[j] = 1;      // 标记时间点被占用
                break;          // 找到时间点,跳出内层循环
            }
        }
        // 如果没有找到合适的时间点,任务不被安排(收益为0)
    }

    // 输出最大总收益
    cout << ans << endl;

    return 0;
}

【运行结果】

7
4 2 4 3 1 4 6
70 60 50 40 30 20 10
230
posted @ 2026-08-10 12:24  团爸讲算法  阅读(22)  评论(0)    收藏  举报