【算法导论】PDD 0928 笔试题解

作者:PAK向日葵日期:2025/10/4

17952869_p0.jpg

快递单号

多多在快递公司负责快递单号录入工作,这些单号有严格的格式要求:

快递单号由3部分组成:2位大写字母(A~Z) + 6位数字 + 1位校验位

校验位计算规则:取前8位(2 字母 + 6 数字)中每个字符的ASCII码之和,对26取余后,加上A的ASCII码,得到的字符即为校验位

现在有一批可能存在校验位错误的单号,请你编写程序:

  • 若单号格式正确且校验位正确,返回原单号
  • 若前 8 位格式正确但校验位错误,返回修复后(校正校验位)的单号
  • 若前 8 位格式错误(非 2 字母 + 6 数字)或快递单号不满足格式要求,返回字符串Invalid

输入描述

共一行,一个字符串(1<=长度<=1024),表示待校验的快递单号

输出描述

共一行,一个字符串,表示修复后的快递单号,若无法修复则返回字符串Invalid

测试用例

示例 1

输入

AB123456C

输出

AB123456Y

说明:

前 8 位字符:A(ASCII码=65)、B(ASCII码=66)、1(ASCII码=49)、2(ASCII码=50)、3(ASCII码=51)、4(ASCII码=52)、5(ASCII码=53)、6(ASCII码=54)

总和:65+66+49+50+51+52+53+54 = 440,440 % 26 = 24,24 + 65 (A的ASCII码) = 89,对应字符Y

因此该单号校验位错误,正确单号应为AB123456Y

示例 2

输入

AC123456Z

输出

AC123456Z

说明:

前 8 位字符:A(ASCII码=65)、C(ASCII码=67)、1(ASCII码=49)、2(ASCII码=50)、3(ASCII码=51)、4(ASCII码=52)、5(ASCII码=53)、6(ASCII码=54)

总和:65+67+49+50+51+52+53+54 = 441,441 % 26 = 25,25 + 65 (A的ASCII码) = 90,对应字符Z

因此该单号校验位正确,直接返回单号AC123456Z

示例 3

输入

AC123M56Z

输出

Invalid

说明:

快递单号由3部分组成:2位大写字母 + 6位数字 + 1位大写字母校验位,不满足规则,直接返回Invalid

参考答案

签到题,秒了。

1#include <iostream>
2#include <limits>
3#include <string>
4
5int main() {
6    std::string input_data;
7    std::cin >> input_data;
8
9    if (input_data.size() != 9) {
10        std::cout << "Invalid" << std::endl;
11        return 0;
12    }
13
14    // 检查前8位格式
15    int letter_cnt = 0;
16    int number_cnt = 0;
17    int sum = 0;
18    for (int i = 0; i < 8; ++i) {
19        if ('A' <= input_data[i] && input_data[i] <= 'Z') {
20            ++letter_cnt;
21        } else if ('0' <= input_data[i] && input_data[i] <= '9') {
22            ++number_cnt;
23        }
24        sum += input_data[i];
25    }
26
27    if (letter_cnt != 2 || number_cnt != 6) {
28        std::cout << "Invalid" << std::endl;
29        return 0;
30    }
31
32    int checker = input_data[8];
33    int target_checker = (sum % 26) + 'A';
34    input_data[8] = target_checker;
35
36    std::cout << input_data;
37}
38

圣诞树

圣诞节快到了,有一棵挂满彩灯的二叉树,需要你来按照图纸装饰。彩灯有5种颜色变化,分别用1-5 表示。1表示 红色, 2表示黄色, 3表示蓝色, 4 表示紫色, 5 表示绿色。

每个节点都一个颜色控制器,每按一下都会产生一个控制信号。控制信号将从当前节点出发向下传递,将当前节点的彩灯以及以当前节点为根节点的子树上的所有节点,切换到下一个颜色( 红 -> 黄 -> 蓝 -> 紫 -> 绿 -> 红 ...) 循环切换。

给定二叉树的初始状态 initial 和 目标状态 target, 两者都以层序遍历产出的一维数组表示。数组元素对应对应位置节点的颜色,0表示该节点没有彩灯。

请给出从initial状态切换至target状态需要的最少控制器点击次数。

注意:

  1. 控制器按一下所产生的控制信号,不只影响当前节点,也会影响以当前节点为根节点的子树上所有节点切换到下一个颜色(最终不一定是同一个颜色)。
  2. 特别地,假设子树上的某个节点X上没有彩灯,则祖先节点处发出的控制信号将不会继续传递给X的后代节点。

输入描述

第一行输入为一个整数n, 代表inital 和 target 数组的大小。

第二行输入为n个整数,代表inital数组。

第三行输入为n个整数,代表target数组。

其他:

  • 如果 initial[i] == 0, 则 target[i] 也一定为0。
  • 1 <=initial.length <= 106

输出描述

一个整数,表示最少点击次数

测试用例

示例 1

输入

5
1 2 3 0 1
2 3 1 0 2

输出

3

示例 2

输入

7
1 2 3 1 2 3 1
3 1 2 3 1 2 1

输出

10

参考答案

借助反证法不难证明,从根节点出发,从上到下依次调整所有彩灯的颜色,是一种可行的点击总次数最少的策略。

不存在比该策略所需点击次数还少的策略。因为假如存在的话,不难得出二叉树上至少存在一个节点的彩灯未被调整至目标颜色的结论,这显然是荒谬的。

代码直接用递归来写就可以了。一是要注意对调整子树根节点颜色对它的后代节点的影响,二是,模运算的表达式别写错了。

要点:

  • 注意对调整子树根节点颜色对它的后代节点的影响
  • 注意如何计算将某个节点从当前颜色调整至目标颜色所需的点击次数
1#include <iostream>
2#include <vector>
3
4// i => 子节点2*i+1,2*i+2
5std::vector<int> tree;
6std::vector<int> target;
7
8int Solve(int root, int inherited) {
9  if (root >= tree.size()) {
10    return 0;
11  }
12
13  int left = 2 * root + 1;
14  int right = 2 * root + 2;
15
16  // 此处没有彩灯
17  if (tree[root] == 0) {
18    return Solve(left, 0) + Solve(right, 0);
19  } else {
20    int current = (tree[root] + inherited - 1) % 5 + 1;
21    int delta = (target[root] - current + 5) % 5;
22    return Solve(left, delta + inherited) + Solve(right, delta + inherited) + delta;
23  }
24}
25
26int main() {
27  int n;
28  std::cin >> n;
29
30  tree.resize(n);
31  target.resize(n);
32  for (int i = 0; i < n; ++i) {
33    std::cin >> tree[i];
34  }
35  for (int i = 0; i < n; ++i) {
36    std::cin >> target[i];
37  }
38
39  std::cout << Solve(0, 0);
40}
41

魔法学院

多多进入了魔法学院学习,学院有 n 门不同的魔法课程,每门课程都有其独特的属性:

  • power[i]:学习这门课程能提升的魔法强度
  • mana[i]:学习这门课程需要消耗的法力值

学院的教学楼有 m 层,每层有不同的环境加成系数 bonus[j](1 ≤ bonus[j] ≤ 3)。

多多总共有 M 点初始法力值。

特殊规则:

  • 顺序学习:多多必须按顺序学习课程(必须先学课程1,再学课程2,以此类推)。
  • 楼层绑定:每门课程只能在某一层完整学习,不能跨层。
  • 强度加成:在第 j 层学习第 i 门课程时,获得的实际魔法强度为 power[i] × bonus[j]
  • 法力消耗:在第 j 层学习第 i 门课程时,消耗的实际法力值为 mana[i] × bonus[j]
  • 切换代价:
    • 多多可以在不同楼层之间切换课程,第一次学习选择楼层没有切换代价, 但每次切换可能会额外消耗楼层高度差的法力值。
    • 如果从低楼层切换到高楼层, 比如从1层切换到4层, 消耗3点法力, 如果从高楼层切换到低楼层, 则不会消耗额外的法力

请求出在满足法力值限制(总法力消耗不超过 M)的条件下,多多能获得的最大魔法强度总和(无需学完所有课程)。

输入描述

第一行三个整数 n, m, M(1 ≤ n ≤ 100, 1 ≤ m ≤ 5, 1 ≤ M ≤ 1000) 第二行 n 个整数,表示 power[i](1 ≤ power[i] ≤ 100) 第三行 n 个整数,表示 mana[i](1 ≤ mana[i] ≤ 100) 第四行 m 个整数,表示 bonus[j](1 ≤ bonus[j] ≤ 3)

输出描述

输出一个整数,表示能获得的最大魔法强度总和。如果无法完成任何课程(例如,第一门课程在任何一层学习的法力消耗都超过 M),则输出 0。

补充说明

对于 20% 的数据:n ≤ 10, 1 ≤ m ≤ 5, 1 ≤ M ≤ 1000

对于 60% 的数据:n ≤ 30, 1 ≤ m ≤ 5, 1 ≤ M ≤ 1000

对于 100% 的数据:1 ≤ n ≤ 100, 1 ≤ m ≤ 5, 1 ≤ M ≤ 1000

示例

示例 1

输入

1 1 5
10
5
2

输出

0

说明:

1门课程,1层楼,初始法力值5 课程强度10,消耗5,楼层加成2

实际消耗 = 5×2 = 10 > 5 (无法学习)

示例 2

输入

2 2 20
5 10
2 3
2 3

输出

45

说明:

  • 2门课程,2层楼,初始法力值20
  • 课程强度: [5, 10],消耗: [2, 3],楼层加成: [2, 3]

可能的方案:

  • 都在1层(加成2): 消耗 = 2×2 + 3×2 = 4+6=10,强度 = 5×2 + 10×2 = 10+20=30
  • 都在2层(加成3): 消耗 = 2×3 + 3×3 = 6+9=15,强度 = 5×3 + 10×3 = 15+30=45
  • 课程1在1层,课程2在2层: 切换消耗 = 2-1=1,总消耗 = 2×2 + 3×3 + 1 = 4+9+1=14,强度 = 5×2 + 10×3 = 10+30=40
  • 课程1在2层,课程2在1层: 切换消耗 = 0,总消耗 = 2×3 + 3×2 = 6+6=12,强度 = 5×3 + 10×2 = 15+20=35

参考答案(高维动态规划)

本题是一个最优化问题,涉及到法力消耗情况、课程、楼层三个状态,优化的目标为让魔法强度总和最大,非常适合使用高维动态规划来解答。

以下是本人编写的代码:

1#include <iostream>
2#include <vector>
3
4#define INF (1 << 30)
5
6static int dp[1002][102][102];
7
8int main() {
9  int n, m, M;  // 课程数,教学楼的层数,总法力
10  std::cin >> n >> m >> M;
11
12  std::vector<int> power(n);  // 学习第i门课程,可以提升的魔法强度
13  std::vector<int> mana(n);  // 学习第i门课程,需要消耗的法力
14  std::vector<int> bonus(m);  // 第i层的环境加成系数
15
16  for (int i = 0; i < n; ++i) {
17    std::cin >> power[i];
18  }
19
20  for (int i = 0; i < n; ++i) {
21    std::cin >> mana[i];
22  }
23
24  for (int i = 0; i < m; ++i) {
25    std::cin >> bonus[i];
26  }
27
28  // dp[i][j][k] 总消耗法力为i,在第j层学完第k门课程后,能得到的最大魔法强度总和
29
30  // 初始化dp数组
31  for (int i = 0; i <= M; ++i) {
32    for (int j = 0; j < m; ++j) {
33      for (int k = 0; k < n; ++k) {
34        dp[i][j][k] = -INF;
35      }
36    }
37  }
38
39  int ans = 0;
40  // 先单独计算学习第0门课能获得的最大魔法强度
41  for (int j = 0; j < m; ++j) {
42    int cost = bonus[j] * mana[0];
43    if (cost <= M) {
44      dp[cost][j][0] = bonus[j] * power[0];
45      ans = std::max(ans, dp[cost][j][0]);
46    }
47  }
48
49  for (int i = 0; i <= M; ++i) {
50    for (int j = 0; j < m; ++j) {
51      for (int k = 1; k < n; ++k) {
52        for (int last_j = 0; last_j < m; ++last_j) {
53          int patch = 0;
54          // 从低层切换到高层
55          if (last_j < j) {
56            patch = j - last_j;
57          }
58
59          int curr_cost = bonus[j] * mana[k] + patch;
60          int curr_power = bonus[j] * power[k];
61          if (i - curr_cost >= 0) {
62            dp[i][j][k] = std::max(dp[i][j][k],
63                                   dp[i - curr_cost][last_j][k - 1] + curr_power);
64            ans = std::max(ans, dp[i][j][k]);
65          }
66        }
67      }
68    }
69  }
70
71  std::cout << ans;
72}
73
74

以下是ai生成的一套解答代码,除了dp数组的定义和遍历顺序不一致外,整体思路其实是相通的:

1n, m, M = map(int, input().split())
2power = list(map(int, input().split()))
3mana = list(map(int, input().split()))
4bonus = list(map(int, input().split()))
5# dp[i][j][k] 表示前i门课程,最后一门在第j层,总法力消耗k时的最大魔法强度
6# 初始化为-1,表示不可达
7dp = [[[-1] * (M + 1) for _ in range(m + 1)] for __ in range(n + 1)]
8dp[0][0][0] = 0
9ans = 0
10for i in range(1, n + 1):
11    for j in range(1, m + 1):
12        cur_mana = mana[i-1] * bonus[j-1]
13        cur_power = power[i-1] * bonus[j-1]
14        # 确定p的范围:i=1时p可以是0,i>1时p必须是1到m
15        if i == 1:
16            p_start, p_end = 0, m
17        else:
18            p_start, p_end = 1, m
19        for p in range(p_start, p_end + 1):
20            # 计算切换成本
21            if p == 0:
22                cost = 0
23            else:
24                if p < j:
25                    cost = j - p
26                else:
27                    cost = 0
28            # 遍历k0
29            for k0 in range(M + 1):
30                if dp[i-1][p][k0] == -1:
31                    continue
32                total = k0 + cost + cur_mana
33                if total <= M:
34                    new_val = dp[i-1][p][k0] + cur_power
35                    if new_val > dp[i][j][total]:
36                        dp[i][j][total] = new_val
37                        if new_val > ans:
38                            ans = new_val
39print(ans)
40

错误解法剖析

下面我们再来看一个某ai大模型生成的错误解法。

1#include <iostream>
2#include <vector>
3#include <algorithm>
4#include <climits>
5
6using namespace std;
7
8int main() {
9  int n, m, M;
10  cin >> n >> m >> M;
11
12  vector<int> power(n + 1), mana(n + 1), bonus(m + 1);
13
14  for (int i = 1; i <= n; i++) cin >> power[i];
15  for (int i = 1; i <= n; i++) cin >> mana[i];
16  for (int i = 1; i <= m; i++) cin >> bonus[i];
17
18  // dp[i][j]:学完第 i 门课,且第 i 门课在楼层 j 时的最大强度
19  // cost[i][j]:对应的最小法力消耗
20  vector<vector<long long>> dp(n + 1, vector<long long>(m + 1, -1));
21  vector<vector<long long>> cost(n + 1, vector<long long>(m + 1, LLONG_MAX));
22
23  // 初始化:没学任何课程时,在任意楼层强度为 0,消耗为 0
24  for (int j = 1; j <= m; j++) {
25    dp[0][j] = 0;
26    cost[0][j] = 0;
27  }
28
29  long long ans = 0;
30  for (int i = 1; i <= n; i++) {
31    for (int j_curr = 1; j_curr <= m; j_curr++) {
32      // 当前课程 i 在楼层 j_curr 的基础消耗和收益
33      int base_cost = mana[i] * bonus[j_curr];
34      int gain = power[i] * bonus[j_curr];
35
36      // 尝试从前一门课 i-1 在楼层 j_prev 转移过来
37      for (int j_prev = 1; j_prev <= m; j_prev++) {
38        if (dp[i - 1][j_prev] == -1) continue; // 前一个状态不可达
39        // 切换楼层代价
40        int switch_cost = 0;
41        if (j_curr > j_prev) {
42          switch_cost = j_curr - j_prev;
43        }
44
45        int total_cost = base_cost + switch_cost;
46        long long new_cost = cost[i - 1][j_prev] + total_cost;
47        if (new_cost <= M) {
48          long long new_power = dp[i - 1][j_prev] + gain;
49
50          // 如果强度更大,或者强度相同但消耗更小,则更新
51          if (new_power > dp[i][j_curr] ||
52             (new_power == dp[i][j_curr] && new_cost < cost[i][j_curr])) {
53            dp[i][j_curr] = new_power;
54            cost[i][j_curr] = new_cost;
55          }
56        }
57      }
58
59      // 如果当前状态可达,更新答案
60      if (dp[i][j_curr] != -1) {
61        if (dp[i][j_curr] > ans) {
62          ans = dp[i][j_curr];
63        }
64      }
65    }
66  }
67
68  // 还要考虑只学部分课程的情况,但这里 dp[i][j] 已经包含所有 i 的情况
69  // 因为我们在循环中已经对每个 i 更新了 ans
70  cout << ans << endl;
71  return 0;
72}
73

这个解法会在以下的测试用例中得出错误结果

5 5 147
20 2 50 49 30
39 2 22 19 44
2 2 3 3 1

这个case的正确答案应该为273,具体计算过程如下:

  1. 在第5层学习第1门课,魔法强度=20 法力=39
  2. 在第4层学习第2门课,魔法强度=20+2*3=26 法力=39+2*3=45
  3. 在第2层学习第3门课,魔法强度=26+50*2=126 法力=45+22*2=45+44=89
  4. 在第3层学习第4门课,魔法强度=126+49*3=273 法力=89+19*3+1=147

而上面的这段代码的输出结果为116,显然不是最优解。问题出在哪儿呢?

事实上,对于每一个状态<i, j>,该算法都会在总消耗不突破M的前提下,尝试选择增加魔法强度最大的方案。这种贪心选择看似能帮助我们尽可能地优化魔法强度的总和,但却忽略了被选方案所引起的总法力消耗对后续选择的影响,从而陷入了"局部最优解"的陷阱。

这个错误,与有的同学会尝试利用贪心算法来解决01背包问题(例如在不超过背包总容量的前提下,尽可能地拿更贵重的物品,而完全不考虑它们的重量对后续决策的影响),在本质上是一致的。

大鱼吃小鱼

多多在玩大鱼吃小鱼的游戏,目前有 n 条鱼,编号从 1 到 n 按顺序排列,第 i 条鱼的血量记为 ai 。

该游戏有以下规则:一条鱼只有在它的血量严格大于(不包含等于)它相邻的鱼时,它才能吃掉这条相邻的鱼,并且增加自身血量,增加值等于被吃掉的鱼的血量。如果没有任何一条鱼的血量严格大于它的邻居,则游戏结束。

例如,有 n=5,a=[2,2,3,1,4]。该过程可能如下进行:

首先,第 3 条鱼吃掉第 2 条鱼。第 3 条鱼的血量变为 5,第 2 条鱼被吃掉。

然后,第 3 条鱼吃掉第 1 条鱼(由于第 2 条鱼已被吃掉,他们现在是相邻的)。第 3 条鱼的血量变为 7,第 1 条鱼被吃掉。

接着,第 5 条鱼吃掉第 4 条鱼。第 5 条鱼的血量变为 5,第 4 条鱼被吃掉。

最后,第 3 条鱼吃掉第 5 条鱼(由于第 4 条鱼已被吃掉,他们现在是相邻的)。第 3 条鱼的血量变为 12,第 5 条鱼被吃掉。

请你设计一个程序,用于求解:对于每一条鱼,计算在所有可能的进食顺序中,它被其他鱼吃掉所需的最少次数是多少?如果它不可能被吃掉,则输出 −1。

输入描述

共2行,第一行包含一个正整数n,表示鱼的数量。(1<= n <=10^5)

第2行包含n个正整数: a1,a2,...,an(1<= n <=10^5),表示每条鱼的血量。

测试样例中n条鱼的血量之和不会超过10^10。

输出描述

共1行,每行输出 n 个整数。第 i 个整数表示第 i 条鱼被其他鱼吃掉所需的最少次数;如果不可能被吃掉,则输出 −1。

示例 1

输入

4
3 2 4 2

输出

2 1 2 1

示例 2

输入

3
1 2 3

输出

1 1 -1

示例 3

输入

5
2 2 3 1 1

输出

2 1 -1 1 2

示例 4

输入

5
1 3 4 5 12

输出

1 1 1 1 4

参考答案(暴力DFS)

可以用DFS来暴力枚举所有可能的情况。这种解法只能过大约20%的测试用例。

1#include <iostream>
2#include <limits>
3#include <vector>
4
5int FindLeft(std::vector<int64_t>& fish, int i) {
6  int j = i - 1;
7  // 找到左边第一条存活的鱼
8  while (0 <= j && fish[j] <= 0) {
9    --j;
10  }
11  return (0 <= j && 0 < fish[j] && fish[j] < fish[i]) ? j : -1;
12}
13
14int FindRight(std::vector<int64_t>& fish, int i) {
15  int j = i + 1;
16  while (j < fish.size() && fish[j] <= 0) {
17    ++j;
18  }
19  return (j < fish.size() && 0 < fish[j] && fish[j] < fish[i]) ? j : -1;
20}
21
22void Solve(std::vector<int64_t>& fish, int cnt, std::vector<int>& ans) {
23  for (int i = 0; i < fish.size(); ++i) {
24    // 如果当前鱼已经被吃掉,则直接跳过
25    if (fish[i] <= 0) {
26      continue;
27    }
28    // 尝试去吃左边的鱼
29    int left = FindLeft(fish, i);
30    if (left != -1) {
31      int64_t backup = fish[left];
32      ans[left] = std::min(ans[left], cnt);
33      fish[left] = 0;
34      fish[i] += backup;
35      Solve(fish, cnt + 1, ans);
36      fish[left] = backup;
37      fish[i] -= backup;
38    }
39    // 尝试去吃右遍的鱼
40    int right = FindRight(fish, i);
41    if (right != -1) {
42      int64_t backup = fish[right];
43      ans[right] = std::min(ans[right], cnt);
44      fish[right] = 0;
45      fish[i] += backup;
46      Solve(fish, cnt + 1, ans);
47      fish[right] = backup;
48      fish[i] -= backup;
49    }
50  }
51}
52
53int main() {
54  int n;
55  std::cin >> n;
56  std::vector<int64_t> fish(n);
57  for (int i = 0; i < n; ++i) {
58    std::cin >> fish[i];
59  }
60  std::vector<int> ans(n, std::numeric_limits<int>::max());
61  Solve(fish, 1, ans);
62  for (int num : ans) {
63    if (num == std::numeric_limits<int>::max()) {
64      std::cout << -1 << " ";
65    }
66    else {
67      std::cout << num << " ";
68    }
69  }
70}
71

参考答案(BFS)

利用BFS支持求解最短路径的特性。

1#include <iostream>
2#include <vector>
3#include <set>
4#include <queue>
5
6using FishState = std::vector<int64_t>;
7
8int FindLeft(FishState& fish_state, int i) {
9	int j = i - 1;
10	// 向左找到第一条存活的鱼
11	while (0 <= j && fish_state[j] == 0) {
12		--j;
13	}
14	return (0 <= j && fish_state[j] > fish_state[i]) ? j : -1;
15}
16
17int FindRight(FishState& fish_state, int i) {
18	int j = i + 1;
19	// 向左找到第一条存活的鱼
20	while (j < fish_state.size() && fish_state[j] == 0) {
21		++j;
22	}
23	return (j < fish_state.size() && fish_state[j] > fish_state[i]) ? j : -1;
24}
25
26int Solve(FishState& fish_state, int target_fish) {
27	std::queue<std::pair<int, FishState>> q;
28	std::set<FishState> visited;
29
30	q.push({0, fish_state});
31	visited.insert(fish_state);
32
33	int steps = 0;
34	while (!q.empty()) {
35		auto& [steps, curr_fish_state] = q.front();
36
37		if (curr_fish_state[target_fish] == 0) {
38			return steps;
39		}
40
41		// 遍历curr_fish_state,计算下一步可能转移的状态
42		for (int i = 0; i < fish_state.size(); ++i) {
43			// 如果当前鱼已经被吃掉了,跳过
44			if (curr_fish_state[i] == 0) {
45				continue;
46			}
47
48			// 当前鱼被左边的鱼吃掉
49			int left = FindLeft(curr_fish_state, i);
50			if (left != -1) {
51				FishState next_fish_state = curr_fish_state;
52				next_fish_state[left] += next_fish_state[i];
53				next_fish_state[i] = 0;
54				if (!visited.contains(next_fish_state)) {
55					q.push({steps + 1, next_fish_state });
56					visited.insert(next_fish_state);
57				}
58			}
59
60			// 当前鱼被右遍的鱼吃掉
61			int right = FindRight(curr_fish_state, i);
62			if (right != -1) {
63				FishState next_fish_state = curr_fish_state;
64				next_fish_state[right] += next_fish_state[i];
65				next_fish_state[i] = 0;
66				if (!visited.contains(next_fish_state)) {
67					q.push({ steps + 1, next_fish_state });
68					visited.insert(next_fish_state);
69				}
70			}
71		}
72
73		q.pop();
74	}
75
76	return -1;
77}
78
79int main() {
80	int n;
81	std::cin >> n;
82
83	FishState fish_state(n);
84	for (int i = 0; i < n; ++i) {
85		std::cin >> fish_state[i];
86	}
87
88	for (int i = 0; i < n; ++i) {
89		std::cout << Solve(fish_state, i) << " ";
90	}
91}
92

【算法导论】PDD 0928 笔试题解》 是转载文章,点击查看原文


相关推荐


chrome-devtools-mcp windows 环境安装
monkeySix2025/10/2

问题: Could not find Google Chrome executable for channel 'stable' at 'C:\Program Files\Google\Chrome\Application\chrome.exe'. 解决方案: "chrome-devtools": { "command": "npx", "args": [ "chrome-devtools-mcp@latest", "-


C语言趣味小游戏----猜数字小游戏
雨落在了我的手上2025/10/2

一:猜数字游戏的雏形 部分代码如下,这下面的代码只是一个初步的框架,还不能正式的进行猜数字游戏,因为我还没有将猜数字的代码写进去,这里我先给大家解释一下是下面代码的大致意思 首先呢这个猜数字游戏我们要运用do while循环以及switch语句来初步的进行我们的猜数字游戏,当然我们可以不用do while循环,可以用while循环还有就是 for循环,但是这里我推荐大家用do while循环,因为会更加的好理解以及使用 现在我分别拆开代码来和大家一一介绍 int main() { in


Python 的内置函数 aiter
IMPYLH2025/10/2

Python 内建函数列表 > Python 的内置函数 aiter 你是否曾经在异步编程中处理过异步迭代器(Async Iterators)?是否对 async for 循环背后的机制感到好奇?那么,aiter() 就是 Python 提供的一个关键工具,它允许我们更灵活地处理异步可迭代对象(Async Iterables)。 aiter 的函数原型如下: def aiter(async_iterable): ''' 获取异步可迭代对象的迭代器 :param a


改bug的一些体会
花王江不语10/1/2025

这篇摘要总结了代码调试的实用技巧:1)通过构造函数断点快速了解类使用方式;2)代码比对时重点关注首次差异点;3)不熟悉代码时先寻找正确案例对比;4)调试和日志要灵活运用,循环场景适合日志,测试机问题可通过动态库加日志解决;5)平衡调试与代码阅读,以理解代码为目的;6)始终围绕线索展开分析,避免偏离核心问题。这些方法强调务实、聚焦和工具选择的灵活性,帮助开发者高效定位和解决问题。


【开题答辩全过程】以 SpringBootVue的旅游租车管理系统为例,包含答辩的问题和答案
毕设源码-钟学长9/30/2025

这是一篇关于毕业设计答辩的纪实内容,主要记录了&quot;基于SpringBoot+Vue的旅游租车管理系统&quot;的答辩过程。答辩学生详细介绍了系统功能(租车+旅游推荐)、技术架构(SpringBoot+Vue+MySQL)和关键实现(库存控制、登录认证等)。评委老师针对系统设计、数据一致性、推荐算法等提出专业问题,学生给出了具体的技术解决方案。最后评委肯定选题价值和技术实现,建议完善旅游信息模块。文末提供开题报告参考和毕设指导服务,适合处于开题阶段的学生参考使用。全文突出了系统设计细节和答辩应对技巧


Qwen3 Omni 的“全模态”,到底和多模态有啥不一样?
飞哥数智谈2025/10/5

前一阵阿里云栖大会,其中有个发布内容是全模态大模型 Qwen3-Omni。 说实话,这是我第一次真正地审视“全模态大模型”这个概念,因为之前 Qwen2.5-Omni 发布的时候,有了解过,但不多。 简介 先从概念上简单介绍下什么是“全模态大模型”。 “Qwen3-Omni是新一代原生全模态大模型,能够无缝处理文本、图像、音频和视频等多种输入形式,并通过实时流式响应同时生成文本与自然语音输出。” 多模态大模型 vs 全模态大模型 接下来,为了更好地理解,我们与“多模态大模型”做个对比。 相同点是


Spring Cloud之负载均衡之LoadBalance
新绿MEHO2025/10/6

目录 负载均衡 问题 步骤 现象  什么是负载均衡? 负载均衡的一些实现 服务端负载均衡 客户端负载均衡 使用Spring Cloud LoadBalance实现负载均衡 负载均衡策略 ​编辑 ​编辑LoadBalancer原理 服务部署 准备环境和数据 服务构建打包 启动服务 上传Jar包到云服务器 启动服务 远程调用访问  负载均衡 问题 上面是我们之前的代码,是根据应用名称获取了服务实例列表,并从列表中选择了一个服务实例。 那如果


【鸿蒙生态共建】一文说清复杂类型数据的非预期输入转换与兜底-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利
俩毛豆2025/10/7

在客户端开发中,你是否曾遇到过这样的困扰:一次看似寻常的网络数据解析,却导致了出人意料的崩溃;一个本该正常的文件读取操作,却返回了难以理解的数据错误。这些问题的根源,往往指向同一环节——数据类型转换。当应用面对网络传输、文件I/O等不可控的数据源时,如何稳健、准确地进行数据解析与转换,就成为保障应用稳定性的第一道防线。 本篇内容是《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》这本书第四章内容的延续,是咱这本书读者的福利,在本篇内容中以模拟多种数据输入,向复杂类型(类、数


Seata分布式事务框架详解与项目实战
IT橘子皮2025/10/9

一、Seata核心架构与原理 Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的分布式事务解决方案,旨在为微服务架构提供高性能、易用性的分布式事务支持。其核心设计理念是"化繁为简",通过封装传统分布式事务模式的复杂性,降低分布式一致性问题的解决门槛。 ​核心组件​: ​TC(Transaction Coordinator)​​:事务协调者,维护全局事务和分支事务的状态,负责协调全局事务提交或回滚 ​TM(


我用亲身经历告诉你,为什么程序员千万别不把英语当回事
oioihoii2025/10/10

年轻人,如果你现在觉得写代码只需要认识 if/else 和 for 循环里的那几个英文单词就够了,那你简直像极了十年前的我。而今天的我,多想回到过去,给那个骄傲自满的自己一记响亮的耳光。 我不是以成功者的姿态来教导你,而是以一个踩过坑、吃过亏、肠子都悔青了的过来人身份,跟你聊聊我最后悔的一件事——没有早点学好英语。 一、工作里吃的哑巴亏,都是我当年脑子进的水 1. “啃”二手资料的酸楚 还记得那次,团队要引入一个热门的新框架。我兴冲冲地找了几篇中文博客,照猫画虎地搞了起来。结果,掉进了一个坑里,

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0