动态规划篇——记忆化搜索与递推

  • 记忆化搜索与递推

定义

在使用动态规划的时候,我们可能会对某些状态进行重复访问。实际上,我们可以通过存储访问数据的方式,避免重复访问某些状态时的重复计算,达到在时间上优化算法的目的。

关于记忆化搜索的联系与区别。

例题

【例 1】 Function

【例 2】 三倍经验

例题分析

【例 1】

分析

近乎是一道裸的 DP (橙题能有什么弯)。但是,如果使用最朴素的 DP,递归的次数将会非常多。

TLE记录

所以,我们要引入记忆化数组,将递归过程中得到的信息储存。再次访问这些数据时,就能避免重复递归(这是一种空间换时间的思想)。

代码

#include <iostream>
using namespace std;
long long remember[25][25][25];
long long w(long long a, long long b, long long c) {
if (a <= 0 || b <= 0 || c <= 0) {
a = 0;
b = 0;
c = 0;
}
if (a > 20 || b > 20 || c > 20) {
a = 20;
b = 20;
c = 20;
}
if (remember[a][b][c]) {
return remember[a][b][c];
}
else {
if (a <= 0 || b <= 0 || c <= 0) {
remember[a][b][c] = w(0, 0, 0);
}
else if (a > 20 || b > 20 || c > 20) {
remember[a][b][c] = w(20, 20, 20);
}
else if (a < b && b < c) {
remember[a][b][c] = w(a, b, c - 1) + w(a, b - 1, c - 1) - w(a, b - 1, c);
}
else {
remember[a][b][c] = w(a - 1, b, c) + w(a - 1, b - 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1);
}
return remember[a][b][c];
}
}
int main() {
long long a, b, c;
remember[0][0][0] = 1;
while (cin >> a >> b >> c) {
if (a == -1 && b == -1 && c == -1) {
break;
}
else {
cout << "w(" << a << ", " << b << ", " << c << ") = ";
cout << w(a, b, c) << endl;
}
}
return 0;
}

【例 2】

分析

首先,如果没有“同时作为一个强大的小朋友,你可以选择金字塔中的不多于 k 个数字让他们成为原来的 3 倍”这一条件,问题则变成[USACO1.5] [IOI1994]数字三角形 Number Triangles

对于这个简化的问题,我们作如下分析:每个点的“来路”为其左上与右上的点,那么我们只需要设 f(i,j) 为路径在第 i 行第 j 列处结束的最大值,可以写出状态转移方程:

f(i,j)=max(f(i1,j),f(i1,j1))+aij

其中 aij 即为第 i 行第 j 列的数。

回到本题。本题的难点即在我们可以选择不多于 k 个数字让他们成为原来的 3 倍(实际上,如果我们选择的次数大于我们的列数,这些多出的次数是没有用的)。我们可以将状态转移方程改为三维的,来描述已经完成的选择的次数。状态转移方程也可以类似地写出:

f(i,j,k)=max(f(i,j,k),f(i1,j,k)+aij,f(i1,j1,k)+aij)

f(i,j,k+1)=max(f(i,j,k+1),f(i1,j,k)+3×aij,f(i1,j1,k)+3×aij)

最后我们只需要在所有的 f(n,j,k) 中找到最大的即可(即遍历最下面一层金字塔的每一个点,对每一个点遍历选择的次数)。

代码

#include <iostream>
#define int long long
using namespace std;
int dp[105][105][105];//dp[i][j][k]表示在第i行第j列,使用k次能力之后的最大值
int a[105][105];
signed main()
{
int n, _k;
cin >> n >> _k;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i; j++)cin >> a[i][j];
}
for (int i = 0; i <= 100; i++)
{
for (int j = 0; j <= 100; j++)
{
for (int k = 0; k <= 100; k++)dp[i][j][k] = -1e10;
}
}
dp[1][1][0] = a[1][1];
dp[1][1][1] = 3 * a[1][1];
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i; j++)
{
for (int k = 0; k <= min(_k, i); k++)
{
dp[i][j][k] = max(dp[i][j][k], max(dp[i - 1][j][k] + a[i][j], dp[i - 1][j - 1][k] + a[i][j]));
dp[i][j][k + 1] = max(dp[i][j][k + 1], max(dp[i - 1][j][k] + 3 * a[i][j], dp[i - 1][j - 1][k] + 3 * a[i][j]));
}
}
}
int ans = dp[n][1][0];
for (int j = 1; j <= n; j++)
{
for (int k = 0; k <= min(_k, n); k++)
{
ans = max(ans, dp[n][j][k]);
}
}
cout << ans << endl;
return 0;
}
posted @   susenyang  阅读(93)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示