题解:洛谷 P1353 Running

【题目来源】

洛谷:P1353 [USACO08JAN] Running S - 洛谷(luogu.com.cn)

【题目描述】

奶牛们打算通过锻炼来培养自己的运动细胞,作为其中的一员,贝茜选择的运动方式是每天进行 \(n\) 分钟的晨跑。在每分钟的开始,贝茜会选择下一分钟是用来跑步还是休息。

贝茜的体力限制了她跑步的距离。更具体地,如果贝茜选择在第 \(i\) 分钟内跑步,她可以在这一分钟内跑 \(d_i\) 米,并且她的疲劳度会增加 \(1\)。不过,无论何时贝茜的疲劳度都不能超过 \(m\)

如果贝茜选择休息,那么她的疲劳度就会每分钟减少 \(1\),但她必须休息到疲劳度恢复到 \(0\) 为止。在疲劳度为 \(0\) 时休息的话,疲劳度不会再变动。晨跑开始时,贝茜的疲劳度为 \(0\)

还有,在 \(n\) 分钟的锻炼结束时,贝茜的疲劳度也必须恢复到 \(0\),否则她将没有足够的精力来对付这一整天中剩下的事情。

请你计算一下,贝茜最多能跑多少米。

【输入】

第一行两个正整数 \(n,m\)
接下来 \(n\) 行,每行一个正整数 \(d_i\)

【输出】

输出一个整数,表示在满足所有限制条件的情况下,贝茜能跑的最大距离。

【输入样例】

5 2
5
3
4
2
10

【输出样例】

9

【核心思想】

  1. 问题分析:贝茜进行 \(n\) 分钟晨跑,每分钟可选择跑步或休息。跑步增加疲劳度并获得距离,休息减少疲劳度但必须恢复到0才能再次跑步。初始和结束时疲劳度都必须为0,且不能超过上限 \(m\)。求最大跑步距离。这是一个带状态跳跃的二维DP问题,需要同时追踪时间和疲劳度两个维度。

  2. 算法选择

    • 二维DPf[i][j] 表示第 \(i\) 分钟,疲劳度为 \(j\) 时的最大跑步距离
    • 状态跳跃:疲劳度从 \(j\) 恢复到0可以直接跳跃 \(j\) 分钟(i+j
    • 三种决策:跑步、休息且疲劳度为0、休息且疲劳度大于0(跳跃)
  3. 关键步骤

    • 初始化f[0][0] = 0,其余为负无穷
    • 状态转移:遍历每分钟 \(i\) 和每种疲劳度 \(j\)
      • 跑步\(j < m\)):f[i+1][j+1] = max(f[i+1][j+1], f[i][j] + d[i+1])
      • 休息且 \(j=0\)f[i+1][0] = max(f[i+1][0], f[i][0])(继续休息)
      • 休息且 \(j>0\)f[i+j][0] = max(f[i+j][0], f[i][j])(跳跃 \(j\) 分钟恢复)
    • 统计答案f[n][0]\(n\) 分钟后疲劳度为0的最大距离)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \times m)\)
    • 空间复杂度:\(O(n \times m)\)
  5. 带状态跳跃的二维DP核心思想

    • 二维状态设计:同时追踪时间 \(i\) 和疲劳度 \(j\) 两个维度
    • 状态跳跃优化:疲劳度从 \(j\) 恢复到0需要 \(j\) 分钟,直接跳到 i+j 位置
    • 三种决策覆盖所有情况:跑步增加疲劳度、休息保持0、休息跳跃恢复
    • 边界约束处理:开始时疲劳度为0、结束时疲劳度为0、不超过上限 \(m\)
    • 适用于带资源消耗和恢复机制的最优化问题

【算法标签】

普及+ #线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 10005, M = 505;  // 定义最大物品数N和最大操作次数M
int n, m;  // 物品数量n,操作次数m
int f[N][M];  // 动态规划数组,f[i][j]表示前i个物品使用j次操作的最大值
int a[N];  // 存储每个物品的值

signed main()  // 主函数
{
    cin >> n >> m;  // 输入物品数量和操作次数
    for (int i=1; i<=n; i++)  // 读取所有物品的值
        cin >> a[i];  // 输入第i个物品的值
    memset(f, -0x3f, sizeof(f));  // 初始化DP数组为负无穷
    f[0][0] = 0;  // 初始状态:0个物品,0次操作,值为0
    for (int i=0; i<n; i++)  // 遍历每个物品
    {
        for (int j=0; j<=m; j++)  // 遍历每种操作次数
        {
            if (f[i][j]<0) continue;  // 如果状态无效则跳过
            f[i+1][j+1] = max(f[i+1][j+1], f[i][j] + a[i+1]);  // 选择当前物品
            if (j==0)  // 如果操作次数为0
                f[i+1][j] = max(f[i+1][j], f[i][j]);  // 不选择当前物品
            else  // 如果操作次数大于0
            {
                if (i+j<=n) f[i+j][0] = max(f[i+j][0], f[i][j]);  // 跳过多个物品
            }
        }
    }
    cout << f[n][0] << endl;  // 输出最终答案
    return 0;  // 程序正常结束
}

【运行结果】

5 2
5
3
4
2
10
9
posted @ 2026-06-25 09:14  团爸讲算法  阅读(8)  评论(0)    收藏  举报