题解:洛谷 P2690 Apple Catching

【题目来源】

洛谷:P2690 [USACO04NOV] Apple Catching G

【题目描述】

很少有人知道奶牛爱吃苹果。农夫约翰的农场上有两棵苹果树(编号为 \(1\)\(2\) ), 每一棵树上都长满了苹果。奶牛贝茜无法摘下树上的苹果,所以她只能等待苹果 从树上落下。但是,由于苹果掉到地上会摔烂,贝茜必须在半空中接住苹果(没有人爱吃摔烂的苹果)。贝茜吃东西很快,她接到苹果后仅用几秒钟就能吃完。每一分钟,两棵苹果树其中的一棵会掉落一个苹果。贝茜已经过了足够的训练, 只要站在树下就一定能接住这棵树上掉落的苹果。同时,贝茜能够在两棵树之间 快速移动(移动时间远少于 \(1\) 分钟),因此当苹果掉落时,她必定站在两棵树其中的一棵下面。此外,奶牛不愿意不停地往返于两棵树之间,因此会错过一些苹果。苹果每分钟掉落一个,共 \(T\)\(1 \le T \le 1000\))分钟,贝茜最多愿意移动 \(W\)\(1 \le W \le 30\)) 次。现给出每分钟掉落苹果的树的编号,要求判定贝茜能够接住的最多苹果数。 开始时贝茜在 1 号树下。

【输入】

第一行 \(2\) 个数, \(T\)\(W\)。接下来的 \(t\) 行,每行一个数,代表在时刻 \(t\) 苹果是从 \(1\) 号苹果树还是从 \(2\) 号苹果树上掉下来的。

【输出】

对于每个测试点,输出一行,一个数,为奶牛最多接到的苹果的数量。

【输入样例】

7 2
2
1
1
2
2
1
1

【输出样例】

6

【核心思想】

  1. 问题分析:给定 \(T\) 分钟内每分钟苹果掉落的树编号 \(a_i \in \{1, 2\}\),贝茜初始在 \(1\) 号树下,最多移动 \(W\) 次。每分钟若贝茜所在树下有苹果掉落即可接住。求最多能接住的苹果数。这是一个线性 DP 问题,关键在于状态设计需同时记录时间、已用移动次数和当前位置。

  2. 算法选择

    • 线性 DP(一维扩展):按时间顺序递推,状态包含"已用移动次数"和"当前位置"两个维度
    • 状态转移:每分钟有两种选择——留在原地(不消耗移动次数)或从另一棵树移动过来(消耗 \(1\) 次移动次数)
  3. 关键步骤

    • 初始化:读取 \(T\)(总分钟数)、\(W\)(最大移动次数)、\(a[1..T]\)(每分钟掉落苹果的树编号)
    • DP 状态定义\(f[i][j][k]\) 表示前 \(i\) 分钟移动了 \(j\) 次,当前在树 \(k+1\) 下(\(k=0\) 表示树 \(1\)\(k=1\) 表示树 \(2\))接到的最多苹果数
    • 初始状态\(f[0][0][0] = 0\)(第 \(0\) 分钟在树 \(1\) 下,移动 \(0\) 次,接到 \(0\) 个),其余状态为 \(-\infty\)
    • 状态转移(遍历 \(i\)\(1\)\(T\)\(j\)\(0\)\(W\)):
      • 不移动\(f[i][j][0] = f[i-1][j][0] + (a_i == 1)\)\(f[i][j][1] = f[i-1][j][1] + (a_i == 2)\)
      • 移动过来(需 \(j \geq 1\)):
        • 从树 \(2\) 移到树 \(1\)\(f[i][j][0] = \max(f[i][j][0], f[i-1][j-1][1] + (a_i == 1))\)
        • 从树 \(1\) 移到树 \(2\)\(f[i][j][1] = \max(f[i][j][1], f[i-1][j-1][0] + (a_i == 2))\)
    • 统计答案\(ans = \max_{0 \leq j \leq W, k \in \{0,1\}} f[T][j][k]\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(T \times W)\),双层循环,每层内部 \(O(1)\) 转移
    • 空间复杂度:\(O(T \times W)\),三维 DP 数组(可滚动优化至 \(O(W)\)
  5. 线性 DP 的核心思想

    • 时间维度递推:按时间顺序处理,每分钟的状态仅依赖于前一分钟的状态,满足无后效性
    • 状态设计技巧:将"当前位置"和"已用资源(移动次数)"纳入状态,将复杂的决策过程转化为状态转移
    • 选择建模:每个时刻只有"不动"和"移动"两种决策,分别对应不同的前置状态,取最大值保证最优性
    • 边界处理:初始位置固定在树 \(1\),通过 \(f[0][0][0] = 0\) 和其余 \(-\infty\) 确保状态合法性
    • 适用于有时间序列约束、资源限制(如移动次数)的最优化问题

【算法标签】

普及 #线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 1005; // 最大时间上限
int t, w, ans=-1; // t:总分钟数, w:最大移动次数, ans:最多接住的苹果数
int a[N]; // a[i]:第i分钟苹果从哪棵树掉落(1或2)
int f[N][35][2]; // f[i][j][k]:前i分钟移动了j次,当前在树k+1下接到的最多苹果数

int main()
{
    cin >> t >> w; // 读入总时间和最大移动次数
    for (int i=1; i<=t; i++) // 读入每分钟苹果掉落的树编号
        cin >> a[i];
    // 初始化DP数组为负无穷,表示不可达状态
    memset(f, -0x3f, sizeof(f));
    f[0][0][0] = 0; // 初始状态:第0分钟在1号树下,移动0次,接到0个苹果

    for (int i=1; i<=t; i++) // 外层循环:枚举每一分钟
        for (int j=0; j<=w; j++) // 中层循环:枚举已使用的移动次数
        {
            // 情况1:不移动,继续留在当前树下
            // 留在1号树下,如果当前苹果从1号树掉落则接住
            f[i][j][0] = f[i-1][j][0] + (a[i]==1);
            // 留在2号树下,如果当前苹果从2号树掉落则接住
            f[i][j][1] = f[i-1][j][1] + (a[i]==2);
            // 情况2:从另一棵树移动过来(消耗一次移动机会)
            // 从2号树移动到1号树,如果当前苹果从1号树掉落则接住
            if (j>=1) f[i][j][0] = max(f[i][j][0], f[i-1][j-1][1] + (a[i]==1));
            // 从1号树移动到2号树,如果当前苹果从2号树掉落则接住
            if (j>=1) f[i][j][1] = max(f[i][j][1], f[i-1][j-1][0] + (a[i]==2));
        }

    // 枚举所有可能的移动次数和最终位置,取最大值
    for (int j=0; j<=w; j++)
        ans = max({ans, f[t][j][0], f[t][j][1]});
    cout << ans << endl; // 输出最多接住的苹果数
    return 0;
}

【运行结果】

7 2
2
1
1
2
2
1
1
6
posted @ 2026-08-10 12:28  团爸讲算法  阅读(6)  评论(0)    收藏  举报