[CP] 初窥 Dynamic Programming

]]]] 从今天开始正式学习 「传说中的 DP」。

首先看了 DP 的基本思想。

然后就开始尝试做题。

第一道题是 198. House Robber - 打家劫舍,因为之前练了挺久的暴搜,所以很快就写出了如下的代码:

class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
int ans = 0, cur = 0;
std::function<void(int, int)> dfs = [&](int k, int ok) {
if (k == n) {
ans = std::max(ans, cur);
return;
}
dfs(k + 1, 1);
if (ok) {
cur += nums[k];
dfs(k + 1, 0);
cur -= nums[k];
}
};
dfs(0, 1);
return ans;
}
};

我知道它必然会超时,因为搜索树的节点总数上界为 2n,而 n 最大可达 100

如何优化?联想到斐波那契数列的计算,我猜想搜索树的一部分结点对应的情况被重复计算了。我画出了 n=4 时,上述暴力算法对应的搜索树:
image

此外,我还发现,因为题目需要的是最大值,对于相同的结点(比如图中的 <3,1>),尽管它们对应的值不一定相同,但并不影响下一步决策,用递推式来表示就是:

f(i,0)=f(i1,1)+nums[i1]f(i,1)=max{(f(i1,0)+f(i1,1)}

其中 f(i,0)f(i,1) 表示选择或不选第 i1 个数所能取得的最大值。

再规定 f(0,0)=f(0,1)=0,只需要一遍扫描就能计算出最终答案 max[f(n1,0),f(n1,1)]!简直就像发现了新大陆!

将上述思想转换为代码(稍微做了些改动):

class Solution {
public:
int dp[103][2];
int rob(vector<int>& nums) {
int n = nums.size(), ans = 0;
dp[0][0] = nums[0], dp[0][1] = 0;
for (int i = 1; i < n; i++) {
dp[i][0] = dp[i - 1][1] + nums[i];
dp[i][1] = std::max(dp[i - 1][0], dp[i - 1][1]);
}
ans = std::max(dp[n - 1][0], dp[n - 1][1]);
return ans;
}
};

image

DP 真是太有意思了!

posted @   ZXPrism  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示