赞
踩
以打家劫舍这个例子开始
其实这个题目的意思就是:给你一个数组,拿取不相邻的数组的值,求所有可能拿取的值中的最大值。
题目分析:
就是自己调用自己,一种暴力枚举的方式。所谓暴力是没有经过任何的优化,枚举了所有的情况的方式。
//从后向前遍历做法
class Solution {
public:
int help(int index){
if(index==0)return nums[0];
if(index==1)return max(nums[0],nums[1]);
int use=help(index-2)+nums[index];//选了当前这个数
int ofuse=help(index-1);//不选当前这个数
return max(use,ofuse);
}
int rob(vector<int>& nums) {
if(nums.size()==0)return 0;
return help(nums.size()-1);
}
};
开始真正的动态规划之前,先记录下记忆化搜索的这种方式。
所谓记忆化搜索,就是对于会重复遍历或者说重复计算的部分,直接用数组或者其他集合去存起来,这样当下一次如果计算到相同的值的时候,就可以免去计算直接拿出这个想要的值了。
//从后向前遍历做法 class Solution { public: int help(vector<int>nums,int index,vector<int>&vis){ if(index==0)return nums[0]; if(index==1)return max(nums[0],nums[1]); if(vis[index]!=-1)return vis[index];//如果不是-1,说明递归计算过了 int use=help(nums,index-2,vis)+nums[index];//选了当前这个数 int ofuse=help(nums,index-1,vis);//不选当前这个数 int m=max(use,ofuse); vis[index]=m;//记忆化存储 return m; } int rob(vector<int>& nums) { vector<int>ans(nums.size(),-1);//初始值为-1,如果递归节点的值已经 if(nums.size()==0)return 0; return help(nums,nums.size()-1,ans); } };
自底向上。将大规模的问题转换为小规模的问题。基本上所有的动态规划都可以使用递归的式子来转换过来。
要素:
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()==0)return 0;
if(nums.size()==1)return nums[0];
int dp[110];
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<nums.size();i++){
dp[i]=max(dp[i-1],dp[i-2]+nums[i]);//可以状态压缩
}
return dp[nums.size()-1];
}
};
介绍
拿到一个题目,如何知道这个题目能用动态规划?
状态是什么要如何判断?如何准确定义?
基本题型
线性DP
子序列和子串
首先是几个需要熟记的点:
入门题目:
经典题目:
0-1背包问题:
问题描述:现在有N件物品,一个背包容量为M,每个物品的体积为v,每个物品的价值是w,现在要求出这N件物品的最大价值。
状态方程分析:像这种先确定是使用一维还是二维,首先物品肯定要算上,另外还有限制条件就是物体的体积,同时考虑问题要求出什么,现在是求出这总共的N件物品的最大价值,可以先考虑第一件物品,第一和第二…同时标注出前i件物品使用多少体积,因此得到dp定义:dp[i][j]:前i件物品在使用体积不超过j的情况下的最大价值。
代码实现:
#include<iostream> using namespace std; const int N = 1010; int n, m;//n物品个数,m是背包的容量 int f[N][N];//最大价值 int v[N], w[N];//V是每个物品的体积,w是每个物品的价值 int main() { cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> v[i] >> w[i]; } //初始化就是0件物品最大价值肯定是0 for (int i = 1; i <= n; i++) { for (int j = 0; j <= m; j++) { //如果不取出这第i件物品,那体积无变化 f[i][j] = f[i - 1][j]; //如果取出这第i件数物品放入背包,首先要保证前i件物品使用的体积大于这第i件物品的体积,不然根本放不下去。 if (j>=v[i]) { //拿出第i件物品放入背包,那现在的就可以正常的由前面一件物品推出来, //同时,同一个i要找出满足最大价值的j,遍历物品结束之后找到n件物品在背包容量m的约束下最大的价值 f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]); } } } int res = 0; //已经算出了不同的i最合适的j了,直接固定n在0-m里面找最合适的i就行了,其实这里直接写f[n][m]就是可以的 for (int i = 0; i <= m; i++) { res=max(res,f[n][i]); } cout << res << endl; return 0; }
状态压缩
#include<iostream> using namespace std; const int N = 1010; int n, m;//n物品个数,m是背包的容量 int f[N];//最大价值 int v[N], w[N];//V是每个物品的体积,w是每个物品的价值 int main() { cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> v[i] >> w[i]; } //初始化就是0件物品最大价值肯定是0 for (int i = 1; i <= n; i++) {//物品个数 for (int j = m; j >= v[i]; j--) {//背包体积变化 f[j] = max(f[j], f[j - v[i]] + w[i]); } } } //已经算出了不同的i最合适的j了,直接固定n在0-m里面找最合适的i就行了,其实这里直接写f[n]就是可以的 cout << f[m]<< endl; return 0; }
遍历顺序:关于内循环遍历背包的容量是从后向前遍历的原因是:如果从前向后遍历,那更新的dp值就会覆盖以前的dp值,当下一步要利用以前的状态去推导的时候就会发生错误,而从后向前更新,不会覆盖以前的dp值。
经典题目:
完全背包问题:
打家劫舍
股票
待补:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。