博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从记忆化搜索到动态规划
阅读量:4647 次
发布时间:2019-06-09

本文共 2803 字,大约阅读时间需要 9 分钟。

 1、要想理解动态规划,首先一定要理解记忆化搜索。

以01背包问题为例:

有 n 个重量和价值为 wi [i] ,v [i] 的物品,从这些物品中挑出重量不超过 t 的物品,求所有选择方案中价值总和的最大值。

首先上的无记忆优化的递归搜索:

#include
using namespace std;const int qq = 1e5 + 10;const int INF = 1e9 + 10;const int MOD = 1e9 + 7;int n,t;int wi[qq];int vi[qq];int rec(int i,int j) // i,j含义:从第i个物品开始挑选总重小于j的部分{ int res; if(i == n) //递归的终结条件,每次一定都会走到i == n res = 0; else if (j < wi[i]) // 如果发现 j < wi[i] 即背包装不下第i个物品,就向下一个物品 i+1 递归 res = rec(i+1,j); else // 如果装的下,在此分叉出两种情况:1.不装这个东西,留着空间装价值vi更大的东西 2.装这个东西,背包的容量减小wi[i] res = max(rec(i+1,j),rec(i+1,j-wi[i])+vi[i]); return res; // 运行到最后返回 res 让递归的上一层判断}int main(){ cin >> n >> t; for(int i=0;i < n;i++) cin >> wi[i] >> vi[i]; cout << rec(0,t) << endl; return 0;}

在这种情况下,递归的缺点暴露,即大量重复的参数调用,因此我们可以做以下的记忆化搜索优化,只需要修改很小一部分。

#include
using namespace std;const int qq = 1e5 + 10;const int INF = 1e9 + 10;const int MOD = 1e9 + 7;int n,t;int wi[qq];int vi[qq];int dp[qq][qq];int rec(int i,int j){ if(dp[i][j] >= 0) //如果发现是已经搜索过的直接返回结果 return dp[i][j]; int res; if(i == n) res = 0; else if (j < wi[i]) res = rec(i+1,j); else res = max(rec(i+1,j),rec(i+1,j-wi[i])+vi[i]); return dp[i][j] = res; // 返回 res 的同时令dp[i][j] = res;}int main(){ memset(dp,-1,sizeof(dp)); //dp初始化 cin >> n >> t; for(int i=0;i < n;i++) cin >> wi[i] >> vi[i]; cout << rec(0,t) << endl; return 0;}

只需要这么一个小小的优化,复杂度缩小到O(NT)

2.从记忆化搜索到动态规划

#include
using namespace std;const int qq = 1e5 + 10;const int INF = 1e9 + 10;const int MOD = 1e9 + 7;int n,t;int wi[qq];int vi[qq];int dp[qq][qq];int main(){ memset(dp,0,sizeof(dp)); cin >> n >> t; for(int i=0;i < n;i++) cin >> wi[i] >> vi[i]; for(int i=n-1;i >= 0;i--) //为什么 i 从 n-1 开始? 想一想递归都是一直运行到最后才开始计算的。 { for(int j=0;j <= t;j++) // j 从 0 到 t 遍历 , 但我有个疑惑:记忆化搜索是每次减wi[i],是不是会比动规 j 遍历的次数少? { if(wi[i] > j) dp[i][j] = dp[i+1][j]; else dp[i][j] = max(dp[i+1][j],dp[i+1][j-wi[i]]+vi[i]); } } cout << dp[0][n] << endl; return 0;}

这个样子的动态规划是完全按照记忆化搜索演变过来的

3.动态规划的各种变形

很多时候你看到的别人的解法都是很简略、美观的代码,其实这些都是基于熟练掌握的基础上浓缩而成的,基本上是无法从中理解的。

此为正序递推式:

for(int i=0;i < n;i++)    {        for(int j=0;i <= t;j++)        {            if(j < wi[i])                dp[i+1][j] = dp[i][j];            else                dp[i+1][j] = max(dp[i][j],dp[i][j-wi[i]]+vi[i]);        }    }

一维DP

for(int i=0;i < n;i++)    {        for(int j=t;j >= wi[i];j--)        {            dp[j] = max(dp[j],dp[j-wi[i]]+vi[i]);        }    }

动态规划是很抽象的,之前我很总是习惯要搞清楚 dp[i][j] 是怎样在数组中演变过来的,后来觉得思考不过来。只要清楚dp[i][j]是什么意思就好了。 

转载于:https://www.cnblogs.com/cunyusup/p/8387787.html

你可能感兴趣的文章
python常用类库总结
查看>>
题解 CF962C 【Make a Square】
查看>>
只读数据文件损坏恢复
查看>>
k8s集群上线web静态网站
查看>>
【转】Impala和Hive的关系
查看>>
IDEA操作git
查看>>
有向图算法之拓扑排序
查看>>
windows 下安装elasticsearch
查看>>
C语言学习12:带参数的main函数,无指定的函数形参,调用库函数处理无指定的函数形参,...
查看>>
禁止某程序联网
查看>>
[LOJ6191][CodeM]配对游戏(概率期望DP)
查看>>
mysql中utf8和utf8mb4区别
查看>>
谈谈源码管理那点事儿(一)——源码管理十诫(转)
查看>>
拒绝switch,程序加速之函数指针数组
查看>>
[你必须知道的.NET]第二十五回:认识元数据和IL(中)
查看>>
.NET中的三种Timer的区别和用法
查看>>
python第三方包安装方法(两种方法)
查看>>
MySQL 索引知识整理(创建高性能的索引)
查看>>
C++ 头文件
查看>>
ZOJ 1008 Gnome Tetravex(DFS)
查看>>