引言
动态规划(DP)是解决最优化问题的一种非常有效的方法。在编程竞赛和面试中,它经常用于解决一系列复杂的最大化、最小化问题。例如,在这道题目中,我们通过动态规划技术帮助小F最大化她在超市购物中的喜爱度,基于特定的购物规则。本文不仅解释了实现算法的思路,也分享了学习动态规划的一些经验和心得。
题目描述
小F正在超市购物,有 n 个商品排成一排,每个商品的价格为 ai,小F对它的喜爱度为 bi。所有商品的价格都是偶数。超市有一个活动:当小F以原价购买某件商品时,她可以用半价购买下一件右边相邻的商品(当然也可以选择以原价购买,这样下一件商品仍有机会半价购买)。然而,如果小F半价购买了一件商品,那么下一件相邻的商品只能原价购买。
给定 x 资金,求小F能获得的最大喜爱度总和。
示例
输入:
n = 4, x = 7, a = [2, 2, 6, 2], b = [3, 4, 5, 1]
输出:
12
解题思路
状态定义
我们用 f[i][j][k] 表示考虑前 i 个商品,当前资金为 j,状态 k(0 表示买了上一件商品,此商品半价,1 表示买得上一件商品,此商品原价,2 表示未买此商品)的情况下,能够获得的最大喜爱度。
转移方程
-
状态 0:当前商品以半价购买
if j - a[i] / 2 >= 0 and f[i - 1][1][j - a[i] / 2] > 0: f[i][0][j] = f[i - 1][1][j - a[i] / 2] + b[i] -
状态 1:当前商品以原价购买
if j - a[i] >= 0: f[i][1][j] = max({f[i - 1][0][j - a[i]] + b[i], f[i - 1][1][j - a[i]] + b[i], f[i - 1][2][j - a[i]] + b[i]}) -
状态 2:当前商品不购买
f[i][2][j] = max({f[i - 1][2][j], f[i - 1][0][j], f[i - 1][1][j]})
初始化
如果 a[0] 价格不超过 x,则只有状态1有效,即购买第一个商品。
if (a[0] <= x) {
f[0][1][a[0]] = b[0];
ret = b[0];
}
最终最大值
在处理过程中,更新最大值:
ret = max({ret, f[i][0][j], f[i][1][j], f[i][2][j]});
代码实现
以下是完整代码:
#include <bits/stdc++.h>
using namespace std;
int solution(int n, int x, vector<int> a, vector<int> b) {
int ret = 0;
vector<vector<vector<int>>> f(n, vector<vector<int>>(3, vector<int>(x + 1, 0)));
// 初始化第一个商品
if (a[0] <= x) {
f[0][1][a[0]] = b[0];
ret = b[0];
}
// 状态转移
for (int i = 1; i < n; i++) {
for (int j = 0; j <= x; j++) {
// 状态 0:当前商品以半价购买
if (j - a[i] / 2 >= 0 && f[i - 1][1][j - a[i] / 2] > 0) {
f[i][0][j] = f[i - 1][1][j - a[i] / 2] + b[i];
}
// 状态 1:当前商品以原价购买
if (j - a[i] >= 0) {
f[i][1][j] = max({f[i - 1][0][j - a[i]] + b[i],
f[i - 1][1][j - a[i]] + b[i], f[i - 1][2][j - a[i]] + b[i]});
}
// 状态 2:当前商品不购买
f[i][2][j] = max({f[i - 1][2][j], f[i - 1][0][j], f[i - 1][1][j]});
// 更新最大喜爱度
ret = max({ret, f[i][0][j], f[i][1][j], f[i][2][j]});
}
}
return ret;
}
int main() {
cout << (solution(4, 7, {2, 2, 6, 2}, {3, 4, 5, 1}) == 12) << endl;
cout << (solution(3, 10, {4, 4, 4}, {2, 3, 5}) == 10) << endl;
cout << (solution(5, 8, {2, 4, 4, 6, 2}, {1, 2, 3, 4, 5}) == 10) << endl;
return 0;
}
知识总结与学习方法
动态规划的基本思想
动态规划的核心思想是将复杂问题分解为子问题,通过求解子问题最优解来构建原问题的最优解。其步骤可以简化为以下几步:
- 状态定义:确定用哪些变量来表示原问题和子问题的状态。
- 状态转移方程:给出如何根据子问题的解来构建原问题的解。
- 初始化:合理设置初始条件。
- 计算顺序:自底向上或者自顶向下的计算顺序。
刷题经验
- 找变量和状态:这是动态规划问题的基础。明确问题需要几个维度的状态来表示。
- 写转移方程:对于每个状态,考虑其如何由之前的状态转移过来,列出相应的转移方程。
- 边界条件:考虑特殊情况,设置初始值。
- 优化思路:动态规划表格的大小决定了算法的复杂度,尽量简化状态空间。
学习计划与总结
- 从易到难:从简单的一维 DP 开始,再逐步拓展到二维、三维。初期可结合经典问题,如斐波那契数列、背包问题等逐一练习。
- 动手实践:没有比实际写代码调试更好的学习方法了。准备一个刷题平台(如 leetcode 或豆包 MarsCode),每天挑战几个问题。
- 错题总结:将做错的题目收集整理,分析错误原因,总结正确的思路,以防再犯。
- 资源利用:合理使用学习资源,如刷题网站的解析、视频讲解,以及 AI 辅助工具(如豆包 MarsCode),进一步提高解题效率。
结合这些方法和计划来学习动态规划,将大大提升你解决最优化问题的能力。祝大家在动态规划学习中取得突破,迎接更高级别的挑战!