林老师 · 客观题题库 · CSP-J 学习卷 L28 · 动态规划入门

CSP-J 学习卷 L28 · 动态规划入门

入门级算法 · DP 三性质与选型 / 一维递推·爬楼梯·打家劫舍 / 数字三角形与记忆化 / 0/1·完全·多重·分组背包 / LIS·LCS / 综合与易错
真题
复刻
试卷编号LEARN-J-28
题目总数110 题 · 160 分
试卷类型客观题
考生须知:
① 本卷共 10 大部分,合计 110 题 · 160 分,全部为客观题;
② 试卷右上角设有 「提交答卷」 与 「重置考试」 按钮,提交后系统自动判分并显示答题正确情况,请确认全部作答后再行提交;
③ 试卷不显示答案,提交后方可查看每题作答与正确答案的对照;
④ 答卷进度会保留在本地缓存中,刷新或再次打开仍可继续作答;
⑤ 本卷仅供学生练习使用;请勿用于其他用途;题面有问题请联系:i64coder@163.com。

判 分 报 告

0 / 160 分
0
答 对 · 得 0 分
0
答 错 · 失 0 分
当前筛选下没有题目
壹

DP 思想与三性质

14 计分点 · 每点 1 分
第 1 题 单选 未作答

关于「动态规划」解决问题的基本思想,下面哪种说法正确?

(1 分)
原创教学题 | 考点:DP 基本思想识别
第 2 题 单选 未作答

走楼梯问题:上到第 nn 级台阶,每次可走 1 级或 2 级,问有多少种不同走法。有人想用贪心「每次都尽量走 2 级」。为什么贪心求不出正确的走法数?

01int ways(int n) {
02    if (n == 0 || n == 1) return 1;
03    return ways(n - 1) + ways(n - 2);
04}

(1 分)
原创教学题 | 考点:爬楼梯为什么贪心不行
第 3 题 单选 未作答

下面哪种情况存在明显的「重叠子问题」?

(1 分)
原创教学题 | 考点:重叠子问题识别
第 4 题 单选 未作答

同一句 f(40),下面两个版本一个慢到跑不完、一个瞬间出结果。为什么差距这么大?

01int f(int n) {
02    if (n <= 1) return n;
03    return f(n - 1) + f(n - 2);
04}
05
06int g[100];
07int main() {
08    g[0] = 0; g[1] = 1;
09    for (int i = 2; i <= 40; i++) g[i] = g[i - 1] + g[i - 2];
10}

(1 分)
原创教学题 | 考点:朴素递归为什么指数级
第 5 题 单选 未作答

运行下面的程序,cnt 最终是多少?

01#include <iostream>
02using namespace std;
03int cnt = 0;
04int f(int n) {
05    cnt++;
06    if (n <= 1) return n;
07    return f(n - 1) + f(n - 2);
08}
09int main() {
10    cout << f(6) << " " << cnt << endl;
11    return 0;
12}

(1 分)
原创教学题 | 考点:fib(6) 朴素递归调用次数
第 6 题 单选 未作答

下面哪个描述最准确地表达了「最优子结构」?

(1 分)
原创教学题 | 考点:最优子结构含义
第 7 题 单选 未作答

动态规划里的「无后效性」指的是什么?

(1 分)
原创教学题 | 考点:无后效性含义
第 8 题 单选 未作答

有一个最优化问题,「当前最优」完全取决于之前具体走了哪条路径(比如走过的每一步都会改变未来的收益系数)。这时用一个简单的 dp[i] 一维数组求解,为什么可能出错?

(1 分)
原创教学题 | 考点:DP 什么时候不适用
第 9 题 单选 未作答

分治(如归并排序、快速排序)和动态规划都「把大问题拆成小问题」。两者最本质的区别是什么?

(1 分)
原创教学题 | 考点:分治与 DP 的本质区别
第 10 题 单选 未作答

一个 DP 的状态空间大小为 SS(状态数),每个状态要枚举 MM 种转移。这个 DP 的时间复杂度大约是?

(1 分)
原创教学题 | 考点:DP 时间复杂度口径
第 11 题 单选 未作答

0/1 背包:nn 件物品、容量 VV,用二维表 dp[i][j](ii 行每行 V+1V+1 格),每格从「不选第 ii 件」和「选第 ii 件」两种转移里取较大值。时间复杂度是?

(1 分)
原创教学题 | 考点:背包 DP 复杂度估算
第 12 题 单选 未作答

一个问题想用动态规划求解,下面哪个性质不是必需的?

(1 分)
原创教学题 | 考点:DP 三性质哪个不是必需
第 13 题 单选 未作答

走楼梯问题(一段 nn 级台阶,每次走 1 级或 2 级,问走到第 nn 级有几种走法)、打家劫舍问题(每家有现金,相邻两家不能同时偷,求最大收益),这类问题为什么应该想到用 DP 而不是硬套贪心?

(1 分)
原创教学题 | 考点:贪心不行为什么想 DP
第 14 题 单选 未作答

爬楼梯问题(每次走 1 级或 2 级,求走到第 nn 级的走法数)要算到第 nn 级,状态数组开 int dp[105]。下面哪种说法正确?

(1 分)
原创教学题 | 考点:状态数组大小与状态数
贰

一维递推 DP

26 计分点 · 每点 1 分
第 15 题 单选 未作答

爬楼梯问题(每次走 1 级或 2 级,求走到第 nn 级的走法数)里,状态 dp[i] 应该表示什么?

(1 分)
原创教学题 | 考点:爬楼梯状态定义
第 16 题 单选 未作答

爬楼梯问题(每次走 1 级或 2 级,求走到第 nn 级的走法数)的状态转移方程是?

(1 分)
原创教学题 | 考点:爬楼梯转移方程
第 17 题 单选 未作答

下面的爬楼梯代码(每次走 1 级或 2 级,求走法数)把初值写错了:dp[1] 应该是 1,却误写成了 2。对 n = 3 运行,输出多少?(正确的 dp[3]dp[3] 应为 3)

01#include <iostream>
02using namespace std;
03int main() {
04    int n = 3;
05    int dp[6] = {0};
06    dp[0] = 1; dp[1] = 2;
07    for (int i = 2; i <= n; i++)
08        dp[i] = dp[i - 1] + dp[i - 2];
09    cout << dp[n] << endl;
10    return 0;
11}

(1 分)
原创教学题 | 考点:爬楼梯边界初值
第 18 题 单选 未作答

运行下面的程序,输出多少?

01#include <iostream>
02using namespace std;
03int main() {
04    int n = 7;
05    int dp[10] = {0};
06    dp[0] = 1; dp[1] = 1;
07    for (int i = 2; i <= n; i++)
08        dp[i] = dp[i - 1] + dp[i - 2];
09    cout << dp[n] << endl;
10    return 0;
11}

(1 分)
原创教学题 | 考点:爬楼梯 f(7) 追踪
第 19 题 单选 未作答

为什么 dp[i] = dp[i-1] + dp[i-2] 能把所有走法数全,既不多算也不漏算?

01dp[0] = 1; dp[1] = 1;
02for (int i = 2; i <= n; i++)
03    dp[i] = dp[i - 1] + dp[i - 2];

(1 分)
原创教学题 | 考点:爬楼梯分类为什么不重不漏
第 20 题 单选 未作答

走楼梯问题:一段 nn 级台阶,每次可走 1、2 或 3 级,问走到第 nn 级共有多少种走法。运行下面的程序输出多少?

01#include <iostream>
02using namespace std;
03int main() {
04    int n = 6;
05    int dp[10] = {0};
06    dp[0] = 1; dp[1] = 1; dp[2] = 2;
07    for (int i = 3; i <= n; i++)
08        dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
09    cout << dp[n] << endl;
10    return 0;
11}

(1 分)
原创教学题 | 考点:爬楼梯 1/2/3 阶追踪
第 21 题 单选 未作答

走楼梯问题:一段 nn 级台阶,每次可走 1、2 或 3 级,问走到第 nn 级共有多少种走法。补全下面的转移方程。

01int n = 6;
02int dp[10] = {0};
03dp[0] = 1; dp[1] = 1; dp[2] = 2;
04for (int i = 3; i <= n; i++)
05    dp[i] = ____;
06cout << dp[n] << endl;

(1 分)
原创教学题 | 考点:爬楼梯 1/2/3 阶补全
第 22 题 单选 未作答

打家劫舍问题:一条街 nn 家,第 ii 家有现金 aia_i,相邻两家不能同时偷,求能偷到的最大金额(以 a = {6, 2, 8, 4} 四家为例)。状态 dp[i] 应该表示什么?

(1 分)
原创教学题 | 考点:打家劫舍状态定义
第 23 题 单选 未作答

打家劫舍(第 ii 家有现金 aia_i,相邻两家不能同时偷,求最大金额)。为什么打家劫舍不能写成 dp[i] = dp[i-1] + a[i](每家都在上一家基础上直接加)?用数据 a = {6, 2, 8, 4} 说明。

01dp[0] = a[0];
02for (int i = 1; i < 4; i++)
03    dp[i] = dp[i - 1] + a[i];

(1 分)
原创教学题 | 考点:打家劫舍为什么不能每家住着偷
第 24 题 单选 未作答

运行下面的程序,输出多少?

01#include <iostream>
02using namespace std;
03int main() {
04    int a[4] = {6, 2, 8, 4};
05    int dp[4];
06    dp[0] = a[0];
07    dp[1] = max(a[0], a[1]);
08    for (int i = 2; i < 4; i++)
09        dp[i] = max(dp[i - 1], dp[i - 2] + a[i]);
10    cout << dp[3] << endl;
11    return 0;
12}

(1 分)
原创教学题 | 考点:打家劫舍 {6,2,8,4} 追踪
第 25 题 单选 未作答

打家劫舍(每家有现金 aia_i,相邻两家不能同时偷,求最大金额)。为什么打家劫舍的转移是 dp[i] = max(dp[i-1], dp[i-2] + a[i])?

01dp[0] = a[0];
02dp[1] = max(a[0], a[1]);
03for (int i = 2; i < n; i++)
04    dp[i] = max(dp[i - 1], dp[i - 2] + a[i]);

(1 分)
原创教学题 | 考点:打家劫舍转移为什么取 max
第 26 题 单选 未作答

最小花费爬楼梯(爬到第 ii 级要付 cost[i-1],每次从第 i−1i-1 或 i−2i-2 级跳上来,到顶即可):dp[i] = min(dp[i-1], dp[i-2]) + cost[i-1]。为什么最后返回 min(dp[n], dp[n-1]) 而不是只返回 dp[n]?

01int dp[6] = {0};
02dp[1] = cost[0];
03for (int i = 2; i <= n; i++)
04    dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i - 1];
05return min(dp[n], dp[n - 1]);

(1 分)
原创教学题 | 考点:最小花费爬楼梯为什么返回 min(dp[n], dp[n-1])
第 27 题 单选 未作答

最小花费爬楼梯(每级有花费 cost[],每次从 i−1i-1 或 i−2i-2 级跳上来)里,为什么必须显式写 dp[1] = cost[0],不能依赖数组默认的 0?

01int dp[6] = {0};
02dp[1] = cost[0];
03for (int i = 2; i <= n; i++)
04    dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i - 1];

(1 分)
原创教学题 | 考点:为什么 dp[1]=cost[0] 必须显式初始化
第 28~32 题 阅读程序 (共 5 分) 未作答

题目:给定长度为 nn 的数组 cost,每爬一级台阶要付对应的费用。从地面(第 0 级)出发,每次可以跨 1 级或 2 级,问到达楼顶(越过第 nn 级)所需的最小总费用。

本题数据:n = 5,cost = {6, 3, 9, 2, 8}。

01#include <iostream>
02using namespace std;
03int compute(int cost[], int n) {
04    int dp[6] = {0};
05    dp[1] = cost[0];
06    for (int i = 2; i <= n; i++)
07        dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i - 1];
08    return min(dp[n], dp[n - 1]);
09}
10int main() {
11    int cost[5] = {6, 3, 9, 2, 8};
12    cout << compute(cost, 5) << endl;
13    return 0;
14}

28.

本题第一问:程序最终输出多少?

(1 分)
29.

本题第二问:dp[4] 和 dp[5] 分别是多少?

(1 分)
30.

本题第三问:为什么 dp[0] 保持默认值 0 是必要的?

(1 分)
31.

本题第四问:若把 dp[i - 2] 误写成 dp[i - 3],当 i = 2 时会怎样?

(1 分)
32.

本题第五问:若把最后一行改成 return dp[n](不取 min),程序输出多少?

(1 分)
原创教学题 | 经典·最小花费爬楼梯(真题形态)
第 33 题 单选 未作答

斐波那契递推只用到前两项,于是可以不存数组、只维护两个变量 a、b 滚动前进。这属于哪种优化?

01int a = 0, b = 1;
02for (int i = 2; i <= n; i++) {
03    int c = a + b;
04    a = b;
05    b = c;
06}

(1 分)
原创教学题 | 考点:滚动数组是什么优化
第 34 题 单选 未作答

运行下面的程序,输出多少?

01#include <iostream>
02using namespace std;
03int main() {
04    int a = 0, b = 1;
05    for (int i = 2; i <= 7; i++) {
06        int c = a + b;
07        a = b;
08        b = c;
09    }
10    cout << b << endl;
11    return 0;
12}

(1 分)
原创教学题 | 考点:滚动数组双变量追踪
第 35 题 单选 未作答

为什么斐波那契递推可以不存整个数组,只保留两个变量?

01int a = 0, b = 1;
02for (int i = 2; i <= n; i++) {
03    int c = a + b;
04    a = b;
05    b = c;
06}

(1 分)
原创教学题 | 考点:滚动数组为什么能省空间
第 36~40 题 阅读程序 (共 5 分) 未作答

题目:一条街上 nn 家店,第 ii 家有现金 aia_i,相邻两家不能同时被偷。求能偷到的最大金额。

本题数据:n = 5,a = {2, 7, 9, 3, 1}。

01#include <iostream>
02using namespace std;
03int main() {
04    int a[5] = {2, 7, 9, 3, 1};
05    int n = 5;
06    int dp[5];
07    dp[0] = a[0];
08    dp[1] = max(a[0], a[1]);
09    for (int i = 2; i < n; i++)
10        dp[i] = max(dp[i - 1], dp[i - 2] + a[i]);
11    cout << dp[n - 1] << endl;
12    return 0;
13}

36.

本题第一问:程序最终输出多少?

(1 分)
37.

本题第二问:dp[2]、dp[3]、dp[4] 分别等于多少?

(1 分)
38.

本题第三问:为什么 dp[1] = max(a[0], a[1]) 不能简化成 dp[1] = a[1]?

(1 分)
39.

本题第四问:若数据改成 a = {1, 3, 1, 3, 100},输出多少?

(1 分)
40.

本题第五问:若把 max(dp[i-1], dp[i-2] + a[i]) 误写成 max(dp[i-2], dp[i-2] + a[i])(两个分支都从 i-2 转移),会怎样?

(1 分)
原创教学题 | 经典·打家劫舍
叁

数字三角形与记忆化搜索

17 计分点 · 每点 1 分
第 41 题 单选 未作答

数字三角形从顶到底,每一步可以往左下或右下走,求路径上的最大和。为什么不能用贪心「每步都选大的那个方向」?

       7
     3   8
   8   1   0
 2   7   4   4
4   5   2   6   5

(1 分)
原创教学题 | 考点:数字三角形为什么不能贪心
第 42 题 单选 未作答

数字三角形(从顶到底每步往左下或右下走,求路径最大和)自底向上 DP 中,dp[i][j] 应该表示什么、怎么转移?

(1 分)
原创教学题 | 考点:数字三角形自底向上转移
第 43 题 单选 未作答

运行下面的程序,输出多少?

01#include <iostream>
02using namespace std;
03int a[5][5] = {
04    {7}, {3, 8}, {8, 1, 0}, {2, 7, 4, 4}, {4, 5, 2, 6, 5}
05};
06int main() {
07    for (int i = 3; i >= 0; i--)
08        for (int j = 0; j <= i; j++)
09            a[i][j] += max(a[i + 1][j], a[i + 1][j + 1]);
10    cout << a[0][0] << endl;
11    return 0;
12}

(1 分)
原创教学题 | 考点:大三角自底向上 dp 表
第 44 题 单选 未作答

对下面这个数字三角形做自底向上 DP,程序输出多少?

   2
  3 4
 6 5 7
4 1 8 3

01#include <iostream>
02using namespace std;
03int a[4][4] = {
04    {2}, {3, 4}, {6, 5, 7}, {4, 1, 8, 3}
05};
06int main() {
07    for (int i = 2; i >= 0; i--)
08        for (int j = 0; j <= i; j++)
09            a[i][j] += max(a[i + 1][j], a[i + 1][j + 1]);
10    cout << a[0][0] << endl;
11    return 0;
12}

(1 分)
原创教学题 | 考点:小三角追踪
第 45 题 单选 未作答

把数字三角形自底向上 DP 的 max 全部改成 min,对下面数据输出多少?

    7
   3 8
  8 1 0
 2 7 4 4
4 5 2 6 5

01int a[5][5] = {
02    {7}, {3, 8}, {8, 1, 0}, {2, 7, 4, 4}, {4, 5, 2, 6, 5}
03};
04int main() {
05    for (int i = 3; i >= 0; i--)
06        for (int j = 0; j <= i; j++)
07            a[i][j] += min(a[i + 1][j], a[i + 1][j + 1]);
08    cout << a[0][0] << endl;
09    return 0;
10}

(1 分)
原创教学题 | 考点:数字三角形求最小值
第 46 题 单选 未作答

补全记忆化搜索版数字三角形:dfs(i, j) 返回从 (i,j)(i,j) 到末行的最大和。

01int memo[5][5];
02int dfs(int i, int j) {
03    if (i == 4) return a[4][j];
04    if (memo[i][j] != -1) return memo[i][j];
05    memo[i][j] = a[i][j] + max(dfs(i + 1, j), dfs(i + 1, j + 1));
06    return ____;
07}

(1 分)
原创教学题 | 考点:记忆化搜索模板判空
第 47 题 单选 未作答

记忆化搜索里用 memo[i][j] = -1 表示「还没算过」。为什么不能用默认的 0 表示?

01int memo[5][5];
02int dfs(int i, int j) {
03    if (i == 4) return a[4][j];
04    if (memo[i][j] != -1) return memo[i][j];
05    memo[i][j] = a[i][j] + max(dfs(i + 1, j), dfs(i + 1, j + 1));
06    return memo[i][j];
07}

(1 分)
原创教学题 | 考点:记忆化搜索初值为什么用 -1
第 48 题 单选 未作答

对 5 行数字三角形跑记忆化搜索 dfs(0,0),有多少个状态会真正执行到 memo[i][j] = ... 那一行(不含底层直接返回、不含命中缓存直接返回)?

01int memo[5][5];
02int dfs(int i, int j) {
03    if (i == 4) return a[4][j];
04    if (memo[i][j] != -1) return memo[i][j];
05    memo[i][j] = a[i][j] + max(dfs(i + 1, j), dfs(i + 1, j + 1));
06    return memo[i][j];
07}

(1 分)
原创教学题 | 考点:记忆化搜索真正计算的状态数
第 49 题 单选 未作答

数字三角形(每步往左下或右下走,求最大路径和)用记忆化搜索求解,时间复杂度是多少?

(1 分)
原创教学题 | 考点:记忆化搜索复杂度
第 50 题 单选 未作答

数字三角形用「自底向上的循环 DP」和「自顶向下的记忆化递归」结果相同。为什么它们是等价的?

循环版本:

01for (int i = 3; i >= 0; i--)
02    for (int j = 0; j <= i; j++)
03        a[i][j] += max(a[i + 1][j], a[i + 1][j + 1]);

递归版本:

01int dfs(int i, int j) {
02    if (i == 4) return a[4][j];
03    if (memo[i][j] != -1) return memo[i][j];
04    memo[i][j] = a[i][j] + max(dfs(i + 1, j), dfs(i + 1, j + 1));
05    return memo[i][j];
06}

(1 分)
原创教学题 | 考点:自底向上与自顶向下为什么等价
第 51 题 单选 未作答

关于记忆化搜索和循环递推,下面哪种说法正确?

(1 分)
原创教学题 | 考点:记忆化 vs 递推谁更快
第 52 题 单选 未作答

记忆化搜索能显著提速的前提是什么?

(1 分)
原创教学题 | 考点:记忆化搜索的适用条件
第 53~57 题 阅读程序 (共 5 分) 未作答

题目:给出一个数字三角形,从顶端走到底部,每一步可走左下方或右下方,求路径上的数字和的最大值。

本题数据(5 行):

       7
     3   8
   8   1   0
 2   7   4   4
4   5   2   6   5

01#include <iostream>
02using namespace std;
03int a[5][5] = {
04    {7}, {3, 8}, {8, 1, 0}, {2, 7, 4, 4}, {4, 5, 2, 6, 5}
05};
06int main() {
07    for (int i = 3; i >= 0; i--)
08        for (int j = 0; j <= i; j++)
09            a[i][j] += max(a[i + 1][j], a[i + 1][j + 1]);
10    cout << a[0][0] << endl;
11    return 0;
12}

53.

本题第一问:程序输出多少?(最大路径和)

(1 分)
54.

本题第二问:a[1][1] 在程序运行后变成多少?

(1 分)
55.

本题第三问:若把 i 的循环从 i = 3 改成 i = 0(从顶层往下做自底向上),会怎样?

(1 分)
56.

本题第四问:若把 max 改成 min,程序输出多少?

(1 分)
57.

本题第五问:如果改用记忆化搜索(自顶向下 + memo 判空),得到的结果和循环版一样吗?

(1 分)
原创教学题 | 经典·数字三角形(自底向上 + 记忆化)
肆

0/1 背包

22 计分点 · 每点 1 分
第 58 题 单选 未作答

0/1 背包问题:nn 件物品第 ii 件重 wiw_i、价值 viv_i(每件选或不选),装进容量 VV 的背包求最大总价值,如 (w,v)=(2,3),(3,4),(4,5)(w,v) = (2,3), (3,4), (4,5)、V=7V = 7。状态 dp[i][j] 表示什么?

(1 分)
原创教学题 | 考点:0/1 背包二维状态定义
第 59 题 单选 未作答

两件物品:(w,v)=(2,3),(4,6)(w,v) = (2,3), (4,6),容量 V=6V = 6。用二维 DP 算出 dp[2][6] 等于多少?

01int w[2] = {2, 4}, v[2] = {3, 6};
02int dp[3][7] = {0};
03for (int i = 1; i <= 2; i++)
04    for (int j = 0; j <= 6; j++) {
05        if (j >= w[i - 1])
06            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
07        else dp[i][j] = dp[i - 1][j];
08    }

(1 分)
原创教学题 | 考点:0/1 背包二维转移
第 60 题 单选 未作答

三件物品:(w,v)=(2,3),(3,4),(4,5)(w,v) = (2,3), (3,4), (4,5),容量 V=5V = 5。用二维 DP 算出 dp[3][5] 等于多少?

01int w[3] = {2, 3, 4}, v[3] = {3, 4, 5};
02int dp[4][6] = {0};
03for (int i = 1; i <= 3; i++)
04    for (int j = 0; j <= 5; j++) {
05        if (j >= w[i - 1])
06            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
07        else dp[i][j] = dp[i - 1][j];
08    }

(1 分)
原创教学题 | 考点:背包二维 dp 表数值
第 61~65 题 阅读程序 (共 5 分) 未作答

题目:nn 件物品,第 ii 件重 wiw_i、价值 viv_i,背包容量 VV。每件物品最多选一次,求能装下的最大总价值。

本题数据:w = {2, 3, 4, 5},v = {3, 4, 5, 6},容量 V = 8。

01#include <iostream>
02using namespace std;
03int main() {
04    int w[4] = {2, 3, 4, 5};
05    int v[4] = {3, 4, 5, 6};
06    int V = 8, n = 4;
07    int dp[5][9] = {0};
08    for (int i = 1; i <= n; i++)
09        for (int j = 0; j <= V; j++) {
10            if (j >= w[i - 1])
11                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
12            else dp[i][j] = dp[i - 1][j];
13        }
14    cout << dp[n][V] << endl;
15    return 0;
16}

61.

本题第一问:程序最终输出多少?

(1 分)
62.

本题第二问:dp[4][6] 等于多少?

(1 分)
63.

本题第三问:为什么转移要先判断 j >= w[i-1]?

(1 分)
64.

本题第四问:若把容量改成 V = 9,程序输出多少?

(1 分)
65.

本题第五问:把转移误写成 dp[i][j] = max(dp[i][j-1], dp[i-1][j-w[i-1]] + v[i-1])(「不选」分支改用同一行的 dp[i][j-1]),对一组数据 w = {3, 2}、v = {2, 1}、容量 4 运行,输出会变成多少?

01int w[2] = {3, 2}, v[2] = {2, 1};
02int dp[3][5] = {0};
03for (int i = 1; i <= 2; i++)
04    for (int j = 0; j <= 4; j++) {
05        if (j >= w[i - 1])
06            dp[i][j] = max(dp[i][j - 1], dp[i - 1][j - w[i - 1]] + v[i - 1]);
07        else dp[i][j] = dp[i - 1][j];
08    }

(1 分)
原创教学题 | 经典·0/1 背包二维版
第 66 题 单选 未作答

0/1 背包(每件物品选或不选,求容量 VV 内最大价值)优化成一维数组后,容量循环应该怎么写?

(1 分)
原创教学题 | 考点:滚动数组一维转移
第 67 题 单选 未作答

为什么一维 0/1 背包的容量循环必须倒序?用一个物品(重 2 值 3、容量 4)说明正序会怎样。

倒序写法:

01for (int j = 4; j >= 2; j--) dp[j] = max(dp[j], dp[j - 2] + 3);

正序写法:

01for (int j = 2; j <= 4; j++) dp[j] = max(dp[j], dp[j - 2] + 3);

(1 分)
原创教学题 | 考点:一维倒序为什么对
第 68 题 单选 未作答

0/1 背包(每件至多选一件)的一维版把容量循环改成正序,得到的算法等价于什么?

(1 分)
原创教学题 | 考点:正序就是完全背包
第 69 题 单选 未作答

一件物品重 2 值 3,容量 4,用「正序循环」的背包跑一遍,dp[4] 是多少?

01int dp[5] = {0};
02for (int j = 2; j <= 4; j++)
03    dp[j] = max(dp[j], dp[j - 2] + 3);

(1 分)
原创教学题 | 考点:完全背包追踪
第 70 题 单选 未作答

背包的「恰好装满」变体:要求选出的物品总重恰好等于容量(凑不出即无解)。它的一维 DP,dp 数组的初值应该怎么设?(与「最多能装」的区别)

(1 分)
原创教学题 | 考点:恰好装满与最多装的初值
第 71 题 单选 未作答

物品:(w,v)=(4,10),(5,100)(w,v) = (4,10), (5,100),容量 V=8V = 8。用「最多装」和「恰好装满」两种模型分别计算,答案各是多少?

01int w[2] = {4, 5}, v[2] = {10, 100};

最多装模型初值全 0;恰好装满模型初值 dp[0] = 0、其余 -inf。

(1 分)
原创教学题 | 考点:恰好装满 vs 最多装数值
第 72 题 单选 未作答

继续上题数据(物品 (w,v)=(4,10),(5,100)(w,v)=(4,10),(5,100),容量 V=8V=8):恰好装满版程序怎么判断「8 无法恰好装满」?

(1 分)
原创教学题 | 考点:恰好装满判合法性
第 73 题 单选 未作答

「0/1 背包」名字里的 0/1 表示什么?

(1 分)
原创教学题 | 考点:0/1 背包为什么叫 0/1
第 74 题 单选 未作答

背包容量 V=14V = 14,物品:(w,v)=(10,10),(9,9),(5,5)(w,v) = (10,10), (9,9), (5,5)(价值恰好等于重量)。按「单价相同、重量大价值大」贪心先拿 (10,10)(10,10),剩容量 4 装不下 (9,9)(9,9) 和 (5,5)(5,5)。为什么这不是最优?下面的 0/1 背包 DP 用同一组数据:

01#include <iostream>
02using namespace std;
03int main() {
04    int w[3] = {10, 9, 5};
05    int v[3] = {10, 9, 5};
06    int V = 14;
07    int dp[15] = {0};
08    for (int i = 0; i < 3; i++)
09        for (int j = V; j >= w[i]; j--)
10            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
11    cout << dp[V] << endl;
12    return 0;
13}

(1 分)
原创教学题 | 考点:贪心背包为什么错
第 75 题 单选 未作答

一维写法 dp[j] = max(dp[j], dp[j-w]+v) 只用了 dp[j] 和 dp[j-w] 两个格子,为什么就能覆盖「前 i−1i-1 件」的全部信息?

01for (int i = 1; i <= n; i++)
02    for (int j = V; j >= w[i - 1]; j--)
03        dp[j] = max(dp[j], dp[j - w[i - 1]] + v[i - 1]);

(1 分)
原创教学题 | 考点:背包一维正确性为什么行
第 76 题 单选 未作答

物品:(w,v)=(2,3),(3,4),(4,5),(5,6)(w,v) = (2,3), (3,4), (4,5), (5,6),容量 V=8V = 8,用一维倒序写法跑完,dp[8] 和 dp[7] 分别是多少?

01int w[4] = {2, 3, 4, 5}, v[4] = {3, 4, 5, 6};
02int dp[9] = {0};
03for (int i = 0; i < 4; i++)
04    for (int j = 8; j >= w[i]; j--)
05        dp[j] = max(dp[j], dp[j - w[i]] + v[i]);

(1 分)
原创教学题 | 考点:一维背包数值追踪
第 77 题 单选 未作答

二维 dp[i][j] 优化成一维 dp[j] 时,为什么可以丢掉「物品维」ii?

01int dp[9] = {0};
02for (int i = 0; i < 4; i++)
03    for (int j = 8; j >= w[i]; j--)
04        dp[j] = max(dp[j], dp[j - w[i]] + v[i]);

(1 分)
原创教学题 | 考点:二维转一维为什么行
第 78 题 单选 未作答

补全「恰好装满」0/1 背包的一维转移(NEG 表示不可达)。

01int dp[105];
02for (int j = 1; j <= V; j++) dp[j] = NEG;
03dp[0] = 0;
04for (int i = 0; i < n; i++)
05    for (int j = V; j >= w[i]; j--)
06        dp[j] = ____;

(1 分)
原创教学题 | 考点:背包恰好装满的转移
第 79 题 单选 未作答

「金币无限枚,面值 1、3、4,问能否凑出 6」和「只有一枚 1 元、一枚 3 元、一枚 4 元硬币」这两类问题,分别用什么背包模型?

(1 分)
原创教学题 | 考点:完全背包 vs 0/1 背包选型
伍

完全背包与硬币凑数

11 计分点 · 每点 1 分
第 80 题 单选 未作答

物品:(w,v)=(1,1),(3,8)(w,v) = (1,1), (3,8),容量 V=5V = 5,用完全背包的正序一维写法跑完,dp[5] 是多少?

01int w[2] = {1, 3}, v[2] = {1, 8};
02int dp[6] = {0};
03for (int i = 0; i < 2; i++)
04    for (int j = w[i]; j <= 5; j++)
05        dp[j] = max(dp[j], dp[j - w[i]] + v[i]);

(1 分)
原创教学题 | 考点:完全背包两件物品追踪
第 81 题 单选 未作答

补全完全背包二维版的「选第 ii 件」分支(数据:(w,v)=(1,1),(3,8)(w,v)=(1,1),(3,8),容量 5)。

01int w[2] = {1, 3}, v[2] = {1, 8};
02int dp[3][6] = {0};
03for (int i = 1; i <= 2; i++)
04    for (int j = 0; j <= 5; j++) {
05        if (j >= w[i - 1])
06            dp[i][j] = ____;
07        else dp[i][j] = dp[i - 1][j];
08    }

(1 分)
原创教学题 | 考点:完全背包二维「选」分支
第 82 题 单选 未作答

同一份二维完全背包代码:「选第 ii 件」的分支为什么用本行的 dp[i][j - w[i-1]],而不能像 0/1 背包那样用上一行的 dp[i-1][j - w[i-1]]?

01for (int i = 1; i <= n; i++)
02    for (int j = 0; j <= V; j++) {
03        if (j >= w[i - 1])
04            dp[i][j] = max(dp[i - 1][j], dp[i][j - w[i - 1]] + v[i - 1]);
05        else dp[i][j] = dp[i - 1][j];
06    }

(1 分)
原创教学题 | 考点:完全背包为什么本行引用=无限取
第 83~87 题 阅读程序 (共 5 分) 未作答

题目:面值 1、2、5 的硬币各无限枚,凑出总金额 5,有多少种凑法?(1+2+2 和 2+1+2 算同一种)

01#include <iostream>
02using namespace std;
03int main() {
04    int c[3] = {1, 2, 5};
05    int dp[6] = {0};
06    dp[0] = 1;
07    for (int i = 0; i < 3; i++)
08        for (int j = c[i]; j <= 5; j++)
09            dp[j] += dp[j - c[i]];
10    cout << dp[5] << endl;
11    return 0;
12}

83.

本题第一问:程序输出多少?

(1 分)
84.

本题第二问:为什么 dp[0] 要初始化为 1 而不是 0?

(1 分)
85.

本题第三问:只保留面值 1 和 2(删掉面值 5),凑 5 有几种凑法?

(1 分)
86.

本题第四问:把两重循环换位——容量 j 放外层、面值放内层(其余不变),凑 5 的答案变成多少、为什么?

(1 分)
87.

本题第五问:只跑到面值 1、2 处理完(面值 5 还没进循环)时,dp[4] 是多少?

(1 分)
原创教学题 | 经典·硬币凑方案数
第 88 题 单选 未作答

运行下面的程序,输出多少?

01#include <iostream>
02using namespace std;
03int main() {
04    int c[3] = {1, 5, 7};
05    int T = 10;
06    int dp[11];
07    for (int j = 0; j <= T; j++) dp[j] = 99;
08    dp[0] = 0;
09    for (int i = 0; i < 3; i++)
10        for (int j = c[i]; j <= T; j++)
11            dp[j] = min(dp[j], dp[j - c[i]] + 1);
12    cout << dp[T] << endl;
13    return 0;
14}

(1 分)
原创教学题 | 考点:硬币最少枚数追踪
第 89 题 单选 未作答

硬币面值只有 5 和 7,要凑 3 元——根本凑不出。下面的程序输出 99 而不是 0。初值为什么要这样设?

01#include <iostream>
02using namespace std;
03int main() {
04    int c[2] = {5, 7};
05    int T = 3;
06    int dp[4];
07    for (int j = 0; j <= T; j++) dp[j] = 99;
08    dp[0] = 0;
09    for (int i = 0; i < 2; i++)
10        for (int j = c[i]; j <= T; j++)
11            dp[j] = min(dp[j], dp[j - c[i]] + 1);
12    cout << dp[T] << endl;
13    return 0;
14}

(1 分)
原创教学题 | 考点:最少枚数初值与无解判定
第 90 题 单选 未作答

同一套「面值 1、2、5 凑 5」的完全背包框架:甲程序内层转移写 dp[j] += dp[j - c](初值 dp[0]=1),乙程序写 dp[j] = min(dp[j], dp[j - c] + 1)(初值 dp[0]=0、其余 99)。甲、乙分别求什么?

01dp[0] = 1;
02for (int i = 0; i < 3; i++)
03    for (int j = c[i]; j <= 5; j++)
04        dp[j] += dp[j - c[i]];

(1 分)
原创教学题 | 考点:凑方案数 vs 最少枚数转移辨析
陆

多重背包与分组背包

12 计分点 · 每点 1 分
第 91 题 单选 未作答

0/1 背包每件至多拿 1 件,完全背包每件拿无限件。「多重背包」的「多重」指的是什么?

(1 分)
原创教学题 | 考点:多重背包定义辨析
第 92 题 单选 未作答

一件物品重 2 值 3、最多拿 3 件,容量 V=8V = 8。把它拆成 3 件独立的 0/1 物品后跑 0/1 背包,dp[8] 是多少?

01#include <iostream>
02using namespace std;
03int main() {
04    int V = 8, s = 3, w = 2, v = 3;
05    int dp[9] = {0};
06    for (int k = 0; k < s; k++)
07        for (int j = V; j >= w; j--)
08            dp[j] = max(dp[j], dp[j - w] + v);
09    cout << dp[V] << endl;
10    return 0;
11}

(1 分)
原创教学题 | 考点:多重背包朴素拆分追踪
第 93 题 单选 未作答

多重背包的朴素解法是把第 ii 件复制 sis_i 份、当普通 0/1 物品跑。为什么这样拆是等价的?

01for (int k = 0; k < s; k++)
02    for (int j = V; j >= w; j--)
03        dp[j] = max(dp[j], dp[j - w] + v);

(1 分)
原创教学题 | 考点:多重背包为什么能拆成 0/1 物品
第 94 题 单选 未作答

把「最多 ss 件」按 1,2,4,…1, 2, 4, \dots(最后不够补余数)打包成几件 0/1 物品。为什么这样拆又对又省?

01int cnt = 0;
02for (int k = 1; s > 0; k *= 2) {
03    int take = min(k, s);
04    w2[cnt] = w * take;
05    v2[cnt] = v * take;
06    cnt++;
07    s -= take;
08}

(1 分)
原创教学题 | 考点:二进制拆分为什么用 1,2,4,…
第 95 题 单选 未作答

s=10s = 10 用上面的二进制拆分代码处理,拆出了几件、每件对应几件原物品?

01int cnt = 0;
02for (int k = 1; s > 0; k *= 2) {
03    int take = min(k, s);
04    cnt++;
05    s -= take;
06}

(1 分)
原创教学题 | 考点:二进制拆分件数追踪
第 96 题 单选 未作答

「分组背包」问题里,物品被分成了若干组。它的选取规则是?

(1 分)
原创教学题 | 考点:分组背包定义辨析
第 97~101 题 阅读程序 (共 5 分) 未作答

题目:分组背包:物品分成 2 组,每组至多选 1 件,背包容量 V=7V=7。组 1:(w,v)=(3,5),(4,6)(w,v)=(3,5),(4,6);组 2:(w,v)=(3,4),(2,2)(w,v)=(3,4),(2,2)。求最大总价值。

01#include <iostream>
02using namespace std;
03int main() {
04    int V = 7;
05    int gw[2][2] = {{3, 4}, {3, 2}};
06    int gv[2][2] = {{5, 6}, {4, 2}};
07    int gn[2] = {2, 2};
08    int dp[8] = {0};
09    for (int i = 0; i < 2; i++)
10        for (int j = V; j >= 0; j--)
11            for (int t = 0; t < gn[i]; t++)
12                if (j >= gw[i][t])
13                    dp[j] = max(dp[j], dp[j - gw[i][t]] + gv[i][t]);
14    cout << dp[V] << endl;
15    return 0;
16}

97.

本题第一问:程序输出多少?

(1 分)
98.

本题第二问:程序跑完后 dp[5] 是多少?

(1 分)
99.

本题第三问:为什么容量循环 j 放在组内物品循环 t 的外面?

(1 分)
100.

本题第四问:若把循环改成「组内每件物品各配一轮完整的容量倒序循环」(当成两件独立 0/1 物品),输出变成多少?

(1 分)
101.

本题第五问:容量改成 V=2V=2,输出多少?

(1 分)
原创教学题 | 经典·分组背包
第 102 题 单选 未作答

某物品「最多拿 1000 件」。朴素拆分与二进制拆分分别把它拆成多少件 0/1 物品?

(1 分)
原创教学题 | 考点:朴素拆分 vs 二进制拆分量级
柒

LIS 最长上升子序列

10 计分点 · 每点 1 分
第 103 题 单选 未作答

最长上升子序列 LIS:在数列 aa 中跳着选出一个严格递增的子序列,求最长长度——如 a={2,3,1}a = \{2, 3, 1\} 的答案是 {2,3}\{2, 3\}、长 22。它的 O(n2)O(n^2) 做法双层循环里 dp[i] 的含义是?

(1 分)
原创教学题 | 考点:LIS 状态定义辨析
第 104 题 单选 未作答

LIS 的 O(n2)O(n^2) 代码里,dp[i] 为什么必须定义为「以 aia_i 结尾」的最长上升子序列,而不能定义成「前 ii 个数里最长」?

01for (int i = 0; i < n; i++) {
02    dp[i] = 1;
03    for (int j = 0; j < i; j++)
04        if (a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1);
05}

(1 分)
原创教学题 | 考点:LIS 状态为什么定义为以 a[i] 结尾
第 105 题 单选 未作答

运行下面的程序,输出多少?

01#include <iostream>
02using namespace std;
03int main() {
04    int a[3] = {2, 3, 1};
05    int dp[3];
06    for (int i = 0; i < 3; i++) {
07        dp[i] = 1;
08        for (int j = 0; j < i; j++)
09            if (a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1);
10    }
11    cout << dp[2] << endl;
12    return 0;
13}

(1 分)
原创教学题 | 考点:LIS 结尾口径追踪
第 106~110 题 阅读程序 (共 5 分) 未作答

题目:求数列 a={1,7,3,5,9,4,8}a = \{1, 7, 3, 5, 9, 4, 8\} 的最长严格上升子序列长度。

01#include <iostream>
02using namespace std;
03int main() {
04    int a[7] = {1, 7, 3, 5, 9, 4, 8};
05    int n = 7, dp[7], ans = 0;
06    for (int i = 0; i < n; i++) {
07        dp[i] = 1;
08        for (int j = 0; j < i; j++)
09            if (a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1);
10        if (dp[i] > ans) ans = dp[i];
11    }
12    cout << ans << endl;
13    return 0;
14}

106.

本题第一问:程序输出多少?

(1 分)
107.

本题第二问:dp[3](以 5 结尾)是多少?

(1 分)
108.

本题第三问:答案为什么要用 ans = max(ans, dp[i]) 扫全表,而不是直接输出 dp[n-1]?

(1 分)
109.

本题第四问:把内层条件改成 a[j] <= a[i],换数据 a={2,2,3}a=\{2,2,3\} 跑,输出变成多少、含义是什么?

(1 分)
110.

本题第五问:把最后一个 8 删掉(数组变 6 个数),输出多少?

(1 分)
原创教学题 | 经典·LIS O(n²)
第 111 题 单选 未作答

补全 LIS 内层转移(dp[i] 已初始化为 1)。

01for (int i = 0; i < n; i++) {
02    dp[i] = 1;
03    for (int j = 0; j < i; j++)
04        if (a[j] < a[i])
05            dp[i] = ____;
06}

(1 分)
原创教学题 | 考点:LIS 转移补全
第 112 题 单选 未作答

数列 a={5,6,7,1,2}a = \{5, 6, 7, 1, 2\},用同一套 LIS 代码(答案 = 全表 max),输出多少?

01for (int i = 0; i < n; i++) {
02    dp[i] = 1;
03    for (int j = 0; j < i; j++)
04        if (a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1);
05    if (dp[i] > ans) ans = dp[i];
06}

(1 分)
原创教学题 | 考点:LIS 答案不在末位追踪
捌

LCS 最长公共子序列

10 计分点 · 每点 1 分
第 113 题 单选 未作答

最长公共子序列 LCS:对两个字符串(如 abcde 与 ace,公共子序列 ace),找出按顺序出现在两者中的最长子序列,求其长度。二维 DP:dp[i][j] 表示什么?

(1 分)
原创教学题 | 考点:LCS 状态定义辨析
第 114 题 单选 未作答

运行下面的程序,输出多少?

01#include <iostream>
02#include <cstring>
03using namespace std;
04int main() {
05    char a[] = "abcde", b[] = "ace";
06    int n = strlen(a), m = strlen(b);
07    int dp[7][5] = {0};
08    for (int i = 1; i <= n; i++)
09        for (int j = 1; j <= m; j++) {
10            if (a[i - 1] == b[j - 1])
11                dp[i][j] = dp[i - 1][j - 1] + 1;
12            else
13                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
14        }
15    cout << dp[n][m] << endl;
16    return 0;
17}

(1 分)
原创教学题 | 考点:LCS 小例追踪
第 115~119 题 阅读程序 (共 5 分) 未作答

题目:字符串 aa = abcbdab,bb = bdcaba,求最长公共子序列的长度。

01#include <iostream>
02#include <cstring>
03using namespace std;
04int main() {
05    char a[] = "abcbdab";
06    char b[] = "bdcaba";
07    int n = strlen(a), m = strlen(b);
08    int dp[9][8] = {0};
09    for (int i = 1; i <= n; i++)
10        for (int j = 1; j <= m; j++) {
11            if (a[i - 1] == b[j - 1])
12                dp[i][j] = dp[i - 1][j - 1] + 1;
13            else
14                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
15        }
16    cout << dp[n][m] << endl;
17    return 0;
18}

115.

本题第一问:程序输出多少?

(1 分)
116.

本题第二问:dp[2][2](a 的前 2 个 ab 与 b 的前 2 个 bd)是多少?

(1 分)
117.

本题第三问:不相等时,转移为什么只取 max(dp[i-1][j], dp[i][j-1])(两个「只删一边末字符」的方向),而不需要 dp[i-1][j-1] + 1?

(1 分)
118.

本题第四问:把相等分支误写成 dp[i][j] = dp[i - 1][j - 1](漏了 +1),输出变成多少?

(1 分)
119.

本题第五问:相等时直接写 dp[i][j] = dp[i-1][j-1] + 1,为什么不用再和 max(dp[i-1][j], dp[i][j-1]) 比较一次?

(1 分)
原创教学题 | 经典·LCS
第 120 题 单选 未作答

补全 LCS 的不相等分支。

01for (int i = 1; i <= n; i++)
02    for (int j = 1; j <= m; j++) {
03        if (a[i - 1] == b[j - 1])
04            dp[i][j] = dp[i - 1][j - 1] + 1;
05        else
06            dp[i][j] = ____;
07    }

(1 分)
原创教学题 | 考点:LCS 不相等分支补全
第 121 题 单选 未作答

把两个串对调:aa = ace,bb = abcde(前文小例的镜像),用同一套代码算,输出多少?

01char a[] = "ace", b[] = "abcde";
02int dp[5][8] = {0};
03for (int i = 1; i <= n; i++)
04    for (int j = 1; j <= m; j++) {
05        if (a[i - 1] == b[j - 1])
06            dp[i][j] = dp[i - 1][j - 1] + 1;
07        else
08            dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
09    }

(1 分)
原创教学题 | 考点:LCS 对称性追踪
第 122 题 单选 未作答

「最长公共子序列」和「最长公共子串」的区别是什么?

(1 分)
原创教学题 | 考点:子序列 vs 子串辨析
玖

综合与易错

21 计分点 · 每点 1 分
第 123 题 单选 未作答

有面值 1、3、4 的硬币各无限枚,要凑出 6 元、硬币数最少。贪心「每次尽量用最大面值」会选 4+1+14+1+1 共 3 枚。为什么这不是最优?下面的完全背包 DP(硬币可无限取,正序循环)算最少枚数:

01#include <iostream>
02using namespace std;
03int main() {
04    int c[3] = {1, 3, 4};
05    int T = 6;
06    int dp[7];
07    for (int j = 0; j <= T; j++) dp[j] = 99;
08    dp[0] = 0;
09    for (int i = 0; i < 3; i++)
10        for (int j = c[i]; j <= T; j++)
11            dp[j] = min(dp[j], dp[j - c[i]] + 1);
12    cout << dp[T] << endl;
13    return 0;
14}

(1 分)
原创教学题 | 考点:凑硬币贪心为什么错
第 124 题 单选 未作答

下面的爬楼梯代码(每次走 1 或 2 级,求走法数)有个 bug,n = 6 时输出多少?(正确的 1/2 阶爬楼梯 dp[6]dp[6] 应为 1313)

01#include <iostream>
02using namespace std;
03int main() {
04    int n = 6;
05    int dp[10] = {0};
06    dp[0] = 1; dp[1] = 1;
07    for (int i = 2; i <= n; i++)
08        dp[i] = dp[i - 1] * 2;
09    cout << dp[n] << endl;
10    return 0;
11}

(1 分)
原创教学题 | 考点:爬楼梯误写成 dp[i-1]*2
第 125 题 单选 未作答

青蛙每次可以跳 1 级或 3 级台阶,求跳到第 nn 级的跳法数。运行下面的程序输出多少?

01#include <iostream>
02using namespace std;
03int main() {
04    int n = 9;
05    int dp[12] = {0};
06    dp[0] = 1; dp[1] = 1; dp[2] = 1;
07    for (int i = 3; i <= n; i++)
08        dp[i] = dp[i - 1] + dp[i - 3];
09    cout << dp[n] << endl;
10    return 0;
11}

(1 分)
原创教学题 | 考点:青蛙跳 1/3 阶追踪
第 126 题 单选 未作答

下面的打家劫舍代码(每家有现金 aia_i,相邻两家不能同时偷,求最大金额)把 dp[1] 错写成 dp[1] = a[1](漏掉了和 a[0] 取 max),对 a = {4, 1, 1, 5} 输出多少?(正确代码输出 9)

01#include <iostream>
02using namespace std;
03int main() {
04    int a[4] = {4, 1, 1, 5};
05    int dp[4];
06    dp[0] = a[0];
07    dp[1] = a[1];
08    for (int i = 2; i < 4; i++)
09        dp[i] = max(dp[i - 1], dp[i - 2] + a[i]);
10    cout << dp[3] << endl;
11    return 0;
12}

(1 分)
原创教学题 | 考点:打家劫舍初值漏 max
第 127 题 单选 未作答

一维 0/1 背包的容量循环被误写成 j > w[i](漏了 =),物品重 2 值 3、容量 V=3V = 3 时,dp[2] 是多少?

01int w = 2, v = 3, V = 3;
02int dp[4] = {0};
03for (int j = V; j > w; j--)
04    dp[j] = max(dp[j], dp[j - w] + v);

(1 分)
原创教学题 | 考点:背包容量循环漏等号
第 128 题 单选 未作答

打家劫舍升级成环形:nn 家围成一圈,首尾两家也相邻,不能同时偷。做法是「分别做 rob(去掉第 1 家) 和 rob(去掉最后一家) 两个直线问题,取较大值」。为什么这样拆?看下面的实现(rob 是直线打家劫舍):

01#include <iostream>
02using namespace std;
03int rob(int a[], int n) {
04    if (n == 1) return a[0];
05    int dp[105];
06    dp[0] = a[0];
07    dp[1] = max(a[0], a[1]);
08    for (int i = 2; i < n; i++)
09        dp[i] = max(dp[i - 1], dp[i - 2] + a[i]);
10    return dp[n - 1];
11}
12int main() {
13    int a[3] = {2, 3, 2};
14    int b[2] = {a[1], a[2]};
15    int c[2] = {a[0], a[1]};
16    cout << max(rob(b, 2), rob(c, 2)) << endl;
17    return 0;
18}

(1 分)
原创教学题 | 考点:环形打家劫舍为什么拆两半
第 129 题 单选 未作答

环形打家劫舍(相邻两家不能同时偷,且首尾相邻围成一圈):a = {2, 3, 2} 围成一圈。用「去掉第 1 家 / 去掉最后一家取 max」的方法,答案是多少?

01int rob(vector<int> v);
02int main() {
03    int a[3] = {2, 3, 2};
04    cout << max(rob({3, 2}), rob({2, 3})) << endl;
05    return 0;
06}

(1 分)
原创教学题 | 考点:环形打家劫舍追踪
第 130 题 单选 未作答

从 m×nm \times n 网格左上角走到右下角,每次只能向右或向下走。用 DP 求路径数,状态 dp[i][j] 应该表示什么?

(1 分)
原创教学题 | 考点:网格路径数 DP 状态
第 131~135 题 阅读程序 (共 5 分) 未作答

题目:一个 3×33 \times 3 网格(3 行 3 列),从左上角 (0,0)(0,0) 出发,每次只能向右或向下走,求到达右下角 (2,2)(2,2) 的不同路径数。

01#include <iostream>
02using namespace std;
03int main() {
04    int dp[3][3] = {0};
05    for (int j = 0; j < 3; j++) dp[0][j] = 1;
06    for (int i = 0; i < 3; i++) dp[i][0] = 1;
07    for (int i = 1; i < 3; i++)
08        for (int j = 1; j < 3; j++)
09            dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
10    cout << dp[2][2] << endl;
11    return 0;
12}

131.

本题第一问:程序输出多少?

(1 分)
132.

本题第二问:为什么 dp[0][j] 和 dp[i][0] 要单独赋成 1?

(1 分)
133.

本题第三问:为什么 dp[i][j] = dp[i-1][j] + dp[i][j-1] 不重不漏?

(1 分)
134.

本题第四问:若把网格改成 3×43 \times 4(3 行 4 列),输出多少?

(1 分)
135.

本题第五问:时间复杂度是多少?

(1 分)
原创教学题 | 经典·网格路径数
第 136 题 单选 未作答

C++ 里 int dp[10]; 声明在函数内部且不初始化,下面的说法哪个正确?

(1 分)
原创教学题 | 考点:数组不初始化的后果
第 137 题 单选 未作答

数字三角形记忆化版里漏写了 if (memo[i][j] != -1) return memo[i][j]; 这一行判空。程序还能算对吗?复杂度怎样?

01int memo[5][5];
02int dfs(int i, int j) {
03    if (i == 4) return a[4][j];
04    memo[i][j] = a[i][j] + max(dfs(i + 1, j), dfs(i + 1, j + 1));
05    return memo[i][j];
06}

(1 分)
原创教学题 | 考点:记忆化忘写判空
第 138 题 单选 未作答

爬楼梯(每次 1/2 级)用下面的递推,dp[8]dp[8]、dp[9]dp[9]、dp[10]dp[10] 分别是多少?

01int dp[12] = {0};
02dp[0] = 1; dp[1] = 1;
03for (int i = 2; i <= 10; i++)
04    dp[i] = dp[i - 1] + dp[i - 2];

(1 分)
原创教学题 | 考点:爬楼梯 f(10) 快速追踪
第 139 题 单选 未作答

数字三角形有 10510^5 行时,用「自顶向下记忆化递归」和「自底向上循环」各有什么隐患?

(1 分)
原创教学题 | 考点:递归深度 vs 递推
第 140 题 单选 未作答

为什么背包问题(每件物品选或不选)需要二维状态(前几件 ×\times 容量),而爬楼梯(每次 1 或 2 级,求走法数)一维就够?

爬楼梯:

01int dp[105];
02dp[i] = dp[i - 1] + dp[i - 2];

背包:

01int dp[105][1005];
02dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w] + v);

(1 分)
原创教学题 | 考点:二维状态什么时候用
第 141 题 单选 未作答

拿到一个新 DP 题,正确的基本顺序是什么?

(1 分)
原创教学题 | 考点:综合·一个问题的 DP 检查清单
第 142 题 单选 未作答

为什么 DP 的边界初值错一处,会导致整个表几乎全错,而不是只影响开头几格?

01int dp[6] = {0};
02dp[1] = cost[0];

若误写成 dp[1] = 0。

(1 分)
原创教学题 | 考点:为什么边界错会全盘错
第 143 题 单选 未作答

最小花费爬楼梯(第 ii 级要付 cost[i-1],每次从 i−1i-1 或 i−2i-2 级跳上来):cost = {5, 6, 1, 2, 7},正确版本返回 min(dp[5], dp[4])。先算 dp[1] 到 dp[5],问正确答案是多少?

01int dp[6] = {0};
02dp[1] = cost[0];
03for (int i = 2; i <= 5; i++)
04    dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i - 1];
05int ans = min(dp[5], dp[4]);

(1 分)
原创教学题 | 考点:最小花费爬楼梯 return dp[n] 陷阱回顾
拾

真题检验 · 历年真题(原样罗列)

3 道真题 | 每题 1 分
第 144~149 题 阅读程序 (共 6 分) 未作答

1  #include <algorithm>
2  #include <iostream>
3  #include <limits>
4 
5  using namespace std;
6 
7  const int MAXN = 105;
8  const int MAXK = 105;
9 
10  int h[MAXN][MAXK];
11 
12  int f(int n, int m)
13  {
14      if (m == 1) return n;
15      if (n == 0) return 0;
16 
17      int ret = numeric_limits<int>::max();
18      for (int i = 1; i <= n; i++)
19          ret = min(ret, max(f(n - i, m), f(i - 1, m - 1)) + 1);
20      return ret;
21  }
22 
23  int g(int n, int m)
24  {
25      for (int i = 1; i <= n; i++)
26          h[i][1] = i;
27      for (int j = 1; j <= m; j++)
28          h[0][j] = 0;
29 
30      for (int i = 1; i <= n; i++) {
31          for (int j = 2; j <= m; j++) {
32              h[i][j] = numeric_limits<int>::max();
33              for (int k = 1; k <= i; k++)
34              h[i][j] = min(
35                  h[i][j],
36                  max(h[i - k][j], h[k - 1][j - 1]) + 1);
37          }
38      }
39 
40      return h[n][m];
41  }
42 
43  int main()
44  {
45      int n, m;
46      cin >> n >> m;
47      cout << f(n, m) << endl << g(n, m) << endl;
48      return 0;
49  }

144.

当输入为 7 3 时,第 1919 行用来取最小值的 min 函数执行了 449449 次。( )

(1 分)
145.

输出的两行整数总是相同的。( )

(1 分)
146.

当 m 为 11 时,输出的第一行总为 n。( )

(1 分)
147.

算法 g(n,m) 最为准确的时间复杂度分析结果为( )。

(1 分)
148.

当输入为 20 2 时,输出的第一行为( )。

(1 分)
149.

当输入为 100 100 时,输出的第一行为( )。

(1 分)
CSP-J 2022 · 阅读程序 第22-22-27题 | 知识点 动态规划入门
第 150~154 题 完善程序 (共 5 分) 未作答

试补全程序。

1  #include <iostream>
2  #include <string>
3  #include <vector>
4  using namespace std;
5 
6  int min(int x, int y, int z) {
7      return min(min(x, y), z);
8  }
9 
10  int edit_dist_dp(string str1, string str2) {
11      int m = str1.length();
12      int n = str2.length();
13      vector<vector<int>> dp(m + 1, vector<int>(n + 1));
14 
15      for (int i = 0; i <= m; i++) {
16          for (int j = 0; j <= n; j++) {
17              if (i == 0)
18                  dp[i][j] = ①;
19              else if (j == 0)
20                  dp[i][j] = ②;
21              else if (③)
22                  dp[i][j] =④;
23              else
24                  dp[i][j] = 1 + min(dp[i][j - 1], dp[i - 1][j], ⑤);
25          }
26      }
27      return dp[m][n];
28  }
29 
30  int main() {
31      string str1, str2;
32      cin >> str1 >> str2;
33      cout << "Minimum number of operations: "
34          << edit_dist_dp(str1, str2) << endl;
35      return 0;
36  }

150.

①处应填( )

(1 分)
151.

②处应填( )

(1 分)
152.

③处应填( )

(1 分)
153.

④处应填( )

(1 分)
154.

⑤处应填( )

(1 分)
CSP-J 2023 · 完善程序 第38-38-42题 | 知识点 动态规划入门
第 155~160 题 阅读程序 (共 6 分) 未作答

1  #include <iostream>
2  #include <vector>
3  using namespace std;
4 
5  int compute(vector<int>& cost) {
6    int n = cost.size();
7    vector<int> dp(n+1, 0);
8    dp[1] = cost[0];
9    for (int i = 2; i <= n; i++) {
10      dp[i] = min(dp[i-1], dp[i-2]) + cost[i-1];
11    }
12    return min(dp[n], dp[n-1]);
13  }
14 
15  int main() {
16    int n;
17    cin >> n;
18    vector<int> cost(n);
19    for (int i = 0; i < n; i++) {
20      cin >> cost[i];
21    }
22    cout << compute(cost) << endl;
23    return 0;
24  }

155.

当输入的 cost 数组为 {10, 15, 20} 时,程序的输出为 15。( )

(1 分)
156.

如果将 dp[i-1] 改为 dp[i-3],程序可能会产生编译错误。( )

(1 分)
157.

程序总是输出 cost 数组中最小的元素。( )

(1 分)
158.

当输入的 cost 数组为 {1, 100, 1, 1, 1, 100, 1, 1, 100, 1} 时,程序的输出为( )。

(1 分)
159.

如果输入的 cost 数组为 {10, 15, 30, 5, 5, 10, 20},程序的输出为( )。

(1 分)
160.

若将代码中的 min(dp[i-1], dp[i-2]) + cost[i-1] 修改为 dp[i-1] + cost[i-2],输入 cost 数组为 {5, 10, 15} 时,程序的输出为( )。

(1 分)
CSP-J 2024 · 阅读程序 第21-21-26题 | 知识点 动态规划入门