题解:AtCoder AT_awc0080_c Reward for Carrying Luggage

【题目来源】

AtCoder:C - Reward for Carrying Luggage

【题目描述】

There are \(N\) rooms arranged in a row, numbered Room \(1\), Room \(2\), \(\ldots\), Room \(N\) from left to right. Takahashi visits these rooms one by one in order from Room \(1\) to Room \(N\), and for each room, he chooses to either "accept" or "decline" the job offered there. Each room has exactly one job, and he visits each room exactly once.

Each room \(i\) has a job with type \(T_i\) and reward \(A_i\). There are two types of jobs:

  • Loading job (\(T_i = 1\)): If accepted, he receives reward \(A_i\) and picks up one piece of luggage. The number of pieces of luggage he is carrying increases by \(1\).
  • Unloading job (\(T_i = 0\)): If accepted, he receives reward \(A_i\). If he is carrying one or more pieces of luggage, he puts down one piece, and the number of pieces of luggage he is carrying decreases by \(1\). If he is carrying no luggage, there is nothing to put down, so the number of pieces of luggage remains \(0\) (he still receives the reward).

The number of pieces of luggage Takahashi is carrying is initially \(0\). Throughout the process of accepting and declining jobs, the number of pieces of luggage must not exceed \(K\) at any point. This is because carrying too much luggage would be unmanageable.

Additionally, the number of pieces of luggage must be exactly \(0\) after visiting all rooms (because he cannot go home while still carrying luggage).

Determine the maximum total reward of the jobs Takahashi accepts while satisfying these conditions. Note that it is possible to accept no jobs at all, in which case the total reward is \(0\).

\(N\) 个房间排成一行,从左到右编号为房间 \(1\)、房间 \(2\)、……、房间 \(N\)。高桥按顺序从房间 \(1\) 到房间 \(N\) 依次访问这些房间,对于每个房间,他选择"接受"或"拒绝"那里提供的工作。每个房间恰好有一份工作,他恰好访问每个房间一次。

每个房间 \(i\) 有一份类型为 \(T_i\)、报酬为 \(A_i\) 的工作。有两种类型的工作:

  • 装载工作\(T_i = 1\)):如果接受,他获得报酬 \(A_i\) 并拿起一件行李。他携带的行李件数增加 \(1\)
  • 卸载工作\(T_i = 0\)):如果接受,他获得报酬 \(A_i\)。如果他正携带一件或更多件行李,他放下一件行李,携带的行李件数减少 \(1\)。如果他没有携带任何行李,就没有东西可放下,因此行李件数保持为 \(0\)(他仍然获得报酬)。

高桥初始携带的行李件数为 \(0\)。在接受和拒绝工作的整个过程中,行李件数在任何时刻都不得超过 \(K\)。这是因为携带太多行李会难以管理。

此外,访问完所有房间后,行李件数必须恰好为 \(0\)(因为他不能还带着行李回家)。

确定在满足这些条件的情况下,高桥接受的工作的最大总报酬。注意,有可能不接受任何工作,此时总报酬为 \(0\)

【输入】

\(N\) \(K\)
\(T_1\) \(A_1\)
\(T_2\) \(A_2\)
\(\vdots\)
\(T_N\) \(A_N\)

  • The first line contains the number of rooms \(N\) and the upper limit on the number of pieces of luggage \(K\), separated by a space.
  • From the 2nd line to the \((N + 1)\)-th line, the job information for each room is given.
  • The \((1 + i)\)-th line contains the type \(T_i\) of the job in room \(i\) (\(1\): loading, \(0\): unloading) and the reward \(A_i\), separated by a space.

【输出】

Print in one line the maximum total reward of accepted jobs when choosing which jobs to accept while satisfying the conditions.

【输入样例】

4 1
1 5
0 4
1 3
0 10

【输出样例】

22

【核心思想】

  1. 问题分析:给定 \(N\) 个房间,每个房间提供一份工作,类型 \(T_i \in \{0, 1\}\)\(0\) 为卸载,\(1\) 为装载),报酬为 \(A_i\)。高桥按顺序访问房间,可选择接受或拒绝每份工作。接受装载工作后行李数 \(+1\) 并获得报酬 \(A_i\);接受卸载工作后行李数 \(-1\)(但不小于 \(0\))并获得报酬 \(A_i\)。要求任何时刻行李数不超过 \(K\),且最终行李数恰好为 \(0\)。求最大总报酬。这是一个带状态约束的线性 DP 问题,行李数量是关键状态维度。

  2. 算法选择

    • 线性 DP(二维状态)f[i][j] 表示处理完前 \(i\) 个房间,当前携带 \(j\) 件行李时的最大总报酬
    • 状态机思想:行李数 \(j\) 作为有限状态(\(0 \leq j \leq K\)),每份工作根据类型引起状态转移
  3. 关键步骤

    • 初始化f[0][0] = 0,其余状态设为 \(-\infty\)(表示不可达)
    • 状态转移(对于每个房间 \(i\),从大到小枚举 \(j\)):
      • 拒绝工作f[i][j] = f[i-1][j]
      • 接受装载工作\(T_i = 1\),要求 \(j \geq 1\)):f[i][j] = \max(f[i][j], f[i-1][j-1] + A_i)
      • 接受卸载工作\(T_i = 0\)):
        • \(j + 1 \leq K\)f[i][j] = \max(f[i][j], f[i-1][j+1] + A_i)(有行李时放下一件)
        • f[i][0] = \max(f[i][0], f[i-1][0] + A_i)(无行李时仍可接受,行李数保持 \(0\)
    • 输出答案\max(0, f[N][0])(允许不接受任何工作,报酬为 \(0\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \cdot K)\),每个房间需枚举 \(0\)\(K\) 的所有行李状态
    • 空间复杂度:\(O(N \cdot K)\),可滚动优化至 \(O(K)\)(只保留前一行状态)
  5. 状态约束 DP 的核心思想

    • 状态抽象:将"行李数量"这一物理约束抽象为 DP 的第二维状态, luggage \(\in [0, K]\) 形成有限状态机
    • 边界处理:卸载时行李数为 \(0\) 的特殊处理(f[i][0] 可从 f[i-1][0] 转移),确保状态不越界
    • 滚动优化:由于 f[i] 只依赖 f[i-1],可用一维数组 + 逆序/正序遍历实现 \(O(K)\) 空间
    • 与背包的区别:本题状态转移由工作类型强制决定(+1 或 -1),而非背包中的可选/不选,属于状态机 DP
    • 适用于带资源消耗/恢复约束的序列决策问题(如能量管理、库存调度、括号匹配等)

【算法标签】

线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100005, INF = -1e18;
int n, k;  // n: 天数,k: 初始能量
int t[N], a[N];  // t[i]: 第i天的类型,a[i]: 第i天的能量值
vector<vector<int> > f;  // DP数组

signed main()
{
    cin >> n >> k;  // 输入天数和初始能量
    int sum = 0;
    for (int i = 1; i <= n; i++)  // 输入每天的类型和能量值
    {
        cin >> t[i] >> a[i];
    }
    // 初始化f数组,所有值初始化为-1e18
    f.assign(n + 1, vector<int>(k + 1, INF));

    f[0][0] = 0;  // 初始状态

    for (int i = 1; i <= n; i++)  // 遍历每一天
    {
        for (int j = k; j >= 0; j--)  // 遍历能量值
        {
            f[i][j] = f[i - 1][j];  // 不选择第i天

            if (t[i] == 1)  // 第i天是类型1
            {
                if (j >= 1)  // 能量值大于等于1
                    f[i][j] = max(f[i][j], f[i - 1][j - 1] + a[i]);  // 消耗1点能量获取a[i]
            }
            else  // 第i天是类型2
            {
                if (j + 1 <= k)  // 能量值小于k
                    f[i][j] = max(f[i][j], f[i - 1][j + 1] + a[i]);  // 增加1点能量获取a[i]
                f[i][0] = max(f[i][0], f[i - 1][0] + a[i]);  // 能量值为0时也可以获取a[i]
            }
        }
    }
    cout << max(0LL, f[n][0]) << endl;  // 输出最后一天能量为0时的最大值
    return 0;
}

【运行结果】

4 1
1 5
0 4
1 3
0 10
22
posted @ 2026-06-21 11:24  团爸讲算法  阅读(7)  评论(0)    收藏  举报