题解:学而思编程 小猴拿糖

【题目来源】

学而思编程:小猴拿糖

【题目描述】

小猴面前有 \(n\) 个糖果排成一行,从左到右第 \(i\) 个糖的价值是 \(a_i\)

小猴打算拿走一些糖,但是他不愿意拿走两个相邻的糖。

你要求出,在不同时拿走任意相邻的糖的前提下,小猴最多能拿走多少价值的糖?

【输入】

第一行一个整数 \(T\),表示有 \(T(1≤T≤50)\) 组数据。

接下来的每组数据,第一行是一个整数 \(N(1≤n≤10^5)\),表示一共有 \(n\) 个糖。

第二行是 \(n\) 个被空格分开的正整数,表示每个糖的价值,不超过 \(1000\)

输入量较大,请使用scanf或者cin关闭流同步。

【输出】

对于每组数据,输出一行。

该行包括一个整数,表示在不同时拿走任意相邻的糖的前提下,最多能拿走的糖的价值。

【输入样例】

2
3
1 8 2
4
10 7 6 14

【输出样例】

8
24

【核心思想】

  1. 问题分析:给定 \(n\) 个糖果排成一行,每个糖果有价值 \(a_i\),要求选择若干糖果使得总价值最大,且不能选择相邻的糖果。这是一个经典的线性动态规划问题,类似于"打家劫舍"问题。

  2. 算法选择

    • 线性 DP(一维动态规划):定义状态表示每个位置选或不选的最大价值
    • 状态设计\(dp[i][0]\) 表示不选第 \(i\) 个糖果时的最大价值,\(dp[i][1]\) 表示选第 \(i\) 个糖果时的最大价值
  3. 关键步骤

    • 初始化\(dp[0][0] = dp[0][1] = 0\),表示没有糖果时价值为 \(0\)
    • 状态转移
      • 不选第 \(i\) 个:\(dp[i][0] = \max(dp[i-1][0], dp[i-1][1])\),可以从上一个选或不选转移过来
      • 选第 \(i\) 个:\(dp[i][1] = dp[i-1][0] + a[i]\),只能从上一个不选转移过来(不能相邻)
    • 答案\(\max(dp[n][0], dp[n][1])\),取最后位置选或不选的最大值
  4. 时间/空间复杂度

    • 时间复杂度:\(O(T \cdot n)\)\(T\) 组数据,每组 \(O(n)\) 的 DP 计算
    • 空间复杂度:\(O(n)\),DP 数组存储,可优化至 \(O(1)\)
  5. 线性 DP 的核心思想

    • 状态定义:明确每个位置的状态,本题用二维状态表示选/不选
    • 状态转移:根据约束条件(不能相邻)设计转移方程
    • 最优子结构:当前最优解由子问题的最优解组合而成
    • 空间优化:由于只依赖前一行,可以用滚动数组将空间优化至 \(O(1)\)
    • 适用于序列选择、路径规划等具有线性结构的问题

【算法标签】

线性DP-一维

【代码详解】

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int t, n, a[N], dp[N][2];  // dp[i][0]: 不选第i个, dp[i][1]: 选第i个

int main()
{
    cin >> t;
    while (t--)
    {
        memset(dp, 0, sizeof(dp));  // 重置DP数组
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);  // 读取数组元素
        }
        for (int i = 1; i <= n; i++)
        {
            // 不选第i个元素
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]);
            // 选第i个元素
            dp[i][1] = dp[i - 1][0] + a[i];
        }
        cout << max(dp[n][0], dp[n][1]) << endl;  // 输出最大和
    }
    return 0;
}

【运行结果】

2
3
1 8 2
8
4
10 7 6 14
24
posted @ 2026-06-14 10:46  团爸讲算法  阅读(11)  评论(0)    收藏  举报