C/C++ 剑指 Offer II 115. 重建序列:探索反向思维与动态规划之美
2023-09-03 16:26:55
题目
给定一个长度为 n 的整数序列 a1, a2, ..., an,请重建一个长度为 n + 1 的整数序列 b1, b2, ..., bn+1,其中 bi 的值可以是 ai 的任意一个倍数。
例如,如果给定序列为 [1, 2, 3],那么可以重建的序列包括 [1, 2, 3, 1], [1, 2, 6, 1], [3, 6, 9, 3] 等。
题目整理
- 输入:一个长度为 n 的整数序列 a1, a2, ..., an。
- 输出:一个长度为 n + 1 的整数序列 b1, b2, ..., bn+1,其中 bi 的值可以是 ai 的任意一个倍数。
题目解题思路
这道题乍一看可能会让人感到棘手,但其实我们可以利用反向思维和动态规划来巧妙地解决它。
首先,我们可以从后往前考虑序列 b 的构建。假设我们已经知道了序列 b 的最后若干项,那么我们就可以根据这些已知项来推导出序列 b 的前若干项。
具体来说,我们可以使用动态规划来解决这个问题。我们定义一个状态 dp[i],表示序列 b 的前 i 项是否可以由序列 a 的前 i 项重建而来。
- 如果 dp[i] 为 true,则表示序列 b 的前 i 项可以由序列 a 的前 i 项重建而来。
- 如果 dp[i] 为 false,则表示序列 b 的前 i 项无法由序列 a 的前 i 项重建而来。
我们可以使用以下递推关系来计算 dp[i]:
dp[i] = true if (dp[i-1] and b[i] is a multiple of a[i-1]) or (dp[i-2] and b[i] is a multiple of a[i-2])
其中,dp[i-1] 表示序列 b 的前 i-1 项是否可以由序列 a 的前 i-1 项重建而来,dp[i-2] 表示序列 b 的前 i-2 项是否可以由序列 a 的前 i-2 项重建而来。
如果我们能够求出 dp[n+1],那么我们就知道了序列 b 的前 n+1 项是否可以由序列 a 的前 n+1 项重建而来。
如果 dp[n+1] 为 true,则表示序列 b 可以由序列 a 重建而来,我们可以通过反向推导的方式来找到序列 b 的具体值。
如果 dp[n+1] 为 false,则表示序列 b 无法由序列 a 重建而来,这道题无解。
具体实现
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
vector<int> reconstructSequence(vector<int>& a) {
int n = a.size();
vector<int> b(n + 1);
vector<bool> dp(n + 2);
dp[0] = true;
dp[1] = true;
for (int i = 2; i <= n + 1; i++) {
dp[i] = (dp[i-1] and b[i-1] % a[i-2] == 0) or (dp[i-2] and b[i-2] % a[i-3] == 0);
}
if (!dp[n+1]) {
return {};
}
int index = n;
b[n] = a[n-1];
for (int i = n-1; i >= 1; i--) {
if (dp[i] and b[i] % a[i-1] == 0) {
index = i-1;
b[index] = a[i-1];
}
}
return b;
}
};
int main() {
Solution solution;
vector<int> a = {1, 2, 3};
vector<int> b = solution.reconstructSequence(a);
for (int x : b) {
cout << x << " ";
}
cout << endl;
return 0;
}
代码解释
-
我们首先定义了一个 Solution 类,并在其中定义了一个名为 reconstructSequence 的方法。这个方法的参数是一个整数序列 a,返回值是一个整数序列 b。
-
在 reconstructSequence 方法中,我们首先计算出序列 a 的长度 n,并定义了一个长度为 n + 1 的整数序列 b 和一个长度为 n + 2 的布尔数组 dp。
-
然后,我们使用动态规划来计算 dp[i]。我们从 dp[0] 和 dp[1] 开始计算,然后使用递推关系来计算 dp[2] 到 dp[n+1]。
-
如果 dp[n+1] 为 true,则表示序列 b 可以由序列 a 重建而来。我们通过反向推导的方式来找到序列 b 的具体值。
-
如果 dp[n+1] 为 false,则表示序列 b 无法由序列 a 重建而来,这道题无解。
-
最后,我们返回序列 b。
复杂度分析
- 时间复杂度:O(n^2),其中 n 为序列 a 的长度。这是因为我们在计算 dp[i] 时需要考虑所有可能的组合,因此时间复杂度为 O(n^2)。
- 空间复杂度:O(n),其中 n 为序列 a 的长度。这是因为我们定义了一个长度为 n + 2 的布尔数组 dp 来存储状态。
总结
这道题考察了反向思维和动态规划的结合应用。通过反向思维,我们可以从后往前考虑序列 b 的构建。通过动态规划,我们可以有效地计算出序列 b 的前 i 项是否可以由序列 a 的前 i 项重建而来。这道题的解题思路比较巧妙,值得我们学习。