题解:AtCoder AT_awc0018_e Peak Collection

【题目来源】

AtCoder:E - Peak Collection

【题目描述】

Takahashi is trying to visit the summits of \(N\) mountains lined up in a row along a traverse route. The summits are numbered \(1, 2, \ldots, N\) from the entrance side of the route, and Takahashi starts from the entrance and proceeds one-way from lower-numbered to higher-numbered summits. He cannot return to a summit he has already passed.
高桥正试图沿着一条横贯路线,依次拜访 \(N\) 座排成一列的山峰的山顶。从路线入口侧开始,山顶编号为 \(1, 2, …, N\),高桥从入口出发,从编号较小的山峰单向前往编号较大的山峰。他不能返回已经经过的山顶。

Each summit \(i\) has an "entrance fee" of \(C_i\) yen and an "elevation" of \(S_i\). As Takahashi proceeds along the route, when he reaches each summit, he can choose either to climb it or to pass through without climbing. If he passes through without climbing, no entrance fee is charged. If he climbs it, he must pay that summit's entrance fee.
每座山峰 \(i\) 有"入场费" \(C_i\) 日元和"海拔" \(S_i\)。当高桥沿着路线前进,到达每座山峰时,他可以选择攀登它,或者不攀登而直接通过。如果他不攀登而直接通过,则不收取入场费。如果他攀登,则必须支付该山峰的入场费。

Takahashi has particular preferences for his mountaineering and wants to select which summits to climb such that all of the following conditions are satisfied:
高桥对登山有特别的偏好,他希望选择攀登哪些山顶,以满足以下所有条件:

  • Upper limit on number of climbs: The number of summits climbed must be at most \(K\).
    攀登次数上限:攀登的山顶数量不得超过 \(K\)
  • Strictly increasing elevation: When the climbed summits are listed in ascending order of their numbers, their elevations must be strictly increasing. That is, if he climbs summits \(i_1 < i_2 < \cdots < i_m\) in order, then \(S_{i_1} < S_{i_2} < \cdots < S_{i_m}\) must hold.
    海拔严格递增:当按编号升序列出攀登的山顶时,它们的海拔必须严格递增。也就是说,如果他依次攀登山峰 \(i_1 < i_2 < ⋯ < i_m\),则必须满足 \(S_{i_1} < S_{i_2} < \cdots < S_{i_m}\)
  • Budget constraint: The total entrance fees of the climbed summits must not exceed his funds of \(B\) yen. That is, \(C_{i_1} + C_{i_2} + \cdots + C_{i_m} \leq B\) must hold.
    预算约束:攀登的山顶的总入场费不得超过他的资金 \(B\) 日元。也就是说,必须满足 \(C_{i_1} + C_{i_2} + \cdots + C_{i_m} \leq B\)

Takahashi wants to maximize the number of summits climbed \(m\) while satisfying all the above conditions. Find the maximum value of \(m\). Note that climbing no summits (\(m = 0\)) is also allowed.
高桥希望在满足上述所有条件的情况下,最大化攀登的山顶数量 \(m\)。求 \(m\) 的最大值。注意,不攀登山顶(\(m=0\))也是允许的。

【输入】

\(N\) \(K\) \(B\)
\(C_1\) \(S_1\)
\(C_2\) \(S_2\)
\(\vdots\)
\(C_N\) \(S_N\)

  • The first line contains the number of summits \(N\), the upper limit on climbs \(K\), and the available funds \(B\), separated by spaces.
  • In the following \(N\) lines, the \(i\)-th line (\(1 \leq i \leq N\)) contains the entrance fee \(C_i\) and elevation \(S_i\) of summit \(i\), separated by spaces.

【输出】

Print in one line the maximum number of summits \(m\) that can be climbed while satisfying all the conditions.

【输入样例】

5 3 10
3 100
2 200
5 150
4 300
1 400

【输出样例】

3

【核心思想】

  1. 问题分析:给定 \(N\) 座山峰,每座山峰有入场费 \(C_i\) 和海拔 \(S_i\)。需要选择最多 \(K\) 座山峰攀登,满足海拔严格递增且总费用不超过 \(B\),最大化攀登的山峰数量。这是一个三维约束最长上升子序列问题,关键在于用动态规划同时处理位置、费用、数量三个维度。

  2. 算法选择

    • 线性DPdp[i][j] 表示以第 \(i\) 座山峰结尾、总费用为 \(j\) 时的最大攀登数量
    • 最长上升子序列(LIS)思想:要求海拔严格递增,即 \(S_k < S_i\) 才能从山峰 \(k\) 转移到山峰 \(i\)
    • 费用维度:用费用作为DP的第二维,限制总费用不超过 \(B\)
    • 数量限制:最后统计答案时检查 dp[i][j] <= K
  3. 关键步骤

    • 初始化:对于每座山峰 \(i\),如果 \(C_i \leq B\),则 dp[i][C_i] = 1(只选当前山峰)
    • 状态转移:遍历每座山峰 \(i\) 作为结尾,遍历费用 \(j\),遍历之前所有山峰 \(k\)
      • 如果 \(S_k < S_i\)(海拔递增)且 \(j \geq C_i\)(费用足够):
        • dp[i][j] = max(dp[i][j], dp[k][j - C[i]] + 1)
    • 统计答案:遍历所有 dp[i][j],在满足 dp[i][j] <= K 的条件下取最大值
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^2 \times B)\),三重循环:山峰 \(N\)、费用 \(B\)、之前山峰 \(N\)
    • 空间复杂度:\(O(N \times B)\),DP数组大小
  5. 三维约束DP的核心思想

    • 多维度状态设计:用 dp[i][j] 同时记录结尾位置和费用两个维度
    • LIS扩展:在经典LIS基础上增加费用约束,形成三维约束问题
    • 费用作为资源:将费用视为背包问题中的容量,在有限预算内最大化数量
    • 数量限制后置:先求出所有可能数量,最后筛选满足 \(K\) 限制的最优解
    • 适用于带多重约束的最优化问题

【解题思路】

【算法标签】

线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 505;
int n, k, b;  // n: 物品数量,k: 最大数量限制,b: 预算限制
int c[N], s[N];  // c[i]: 第i个物品的价格,s[i]: 第i个物品的属性值
int dp[N][N], ans;  // dp[i][j]: 以第i个物品结尾,花费为j时的最大数量

int main()
{
    cin >> n >> k >> b;  // 读入物品数量、最大数量限制、预算限制

    for (int i = 1; i <= n; i++)
    {
        cin >> c[i] >> s[i];  // 读入每个物品的价格和属性值
    }

    // 动态规划
    for (int i = 1; i <= n; i++)  // 遍历每个物品作为最后一个物品
    {
        if (c[i] <= b)  // 如果当前物品价格不超过预算
        {
            dp[i][c[i]] = 1;  // 只选择当前物品
        }

        for (int j = 0; j <= b; j++)  // 遍历所有预算
        {
            for (int k = 1; k < i; k++)  // 遍历之前的所有物品
            {
                if (s[k] < s[i] && j >= c[i])  // 如果属性值递增且预算足够
                {
                    // 状态转移:从物品k转移到物品i
                    dp[i][j] = max(dp[i][j], dp[k][j - c[i]] + 1);
                }
            }
        }
    }

    // 寻找满足条件的最大数量
    for (int i = 1; i <= n; i++)  // 遍历所有物品
    {
        for (int j = 0; j <= b; j++)  // 遍历所有预算
        {
            if (dp[i][j] <= k)  // 如果数量不超过限制k
            {
                ans = max(ans, dp[i][j]);  // 更新答案
            }
        }
    }

    cout << ans << endl;  // 输出结果
    return 0;
}

【运行结果】

5 3 10
3 100
2 200
5 150
4 300
1 400
3
posted @ 2026-06-15 09:48  团爸讲算法  阅读(5)  评论(0)    收藏  举报