确定dp数组(dp table)以及下标的含义
确定递推公式
dp数组如何初始化
确定遍历顺序
举例推导dp数组
确定dp数组(dp table)以及下标的含义:第i个斐波那契数的数值为dp[i]
确定递推公式:dp[i]=dp[i-1]+dp[i-2]
dp数组如何初始化:dp[0]=0,dp[1]=1(题目中给出)
确定遍历顺序:从前向后
举例推导dp数组
class Solution {
public:
int fib(int n) {
if(n<=1) return n;
vector dp(n+1);//0~n个数字
dp[0]=0;dp[1]=1;//初始化
for(int i=2;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];//递推公式
}
return dp[n];
}
};
*别忘了边界条件的判断
确定dp数组(dp table)以及下标的含义:达到i阶楼梯有几种方法
确定递推公式:dp[i]=dp[i-1]+dp[i-2]
dp数组如何初始化:dp[0]没有意义,dp[1]=1,dp[2]=2(题目中给出)
确定遍历顺序:由前向后
举例推导dp数组
class Solution {
public:
int climbStairs(int n) {
if(n<=1) return n;
vector dp(n+1);//0~n个数字
dp[0]=0;dp[1]=1;dp[2]=2;//初始化
for(int i=3;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];//递推公式
}
return dp[n];
}
};
确定dp数组(dp table)以及下标的含义:达到下标i的位置所需要的花费为dp[i]
确定递推公式:dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
dp数组如何初始化:(题目中需要向上跳跃才有体力值)dp[1]=0,dp[0]=0
确定遍历顺序:由前向后
举例推导dp数组
class Solution {
public:
int minCostClimbingStairs(vector& cost) {
vector dp(cost.size()+1);//0~n个数字
dp[0]=0;dp[1]=0;//初始化
for(int i=2;i<=cost.size();i++){
dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);//递推公式
}
return dp[cost.size()];
}
};
*比上一题增加了花费
确定dp数组(dp table)以及下标的含义:从(0,0)走到(i,j)有几种不同的走法
确定递推公式:只允许向右和向下走 dp[i][j]=dp[i-1][j]+dp[i][j-1]
dp数组如何初始化:第零行、第零列为1
确定遍历顺序:由左到右、由上到下
举例推导dp数组
class Solution {
public:
int uniquePaths(int m, int n) { //m几行 n几列
vector> dp(m, vector(n, 0));
//初始化 第零行第零列是1 表示有一种方法
for(int i=0;i
**同样的方法,只不过增加了障碍,就无法通过。因此,障碍后的路径都为0
确定dp数组(dp table)以及下标的含义:从(0,0)走到(i,j)有几种不同的走法
确定递推公式:只允许向右和向下走 dp[i][j]=dp[i-1][j]+dp[i][j-1] 如果有障碍就走不了;没有障碍进行递推
dp数组如何初始化:第零行、第零列为1,如遇到障碍,障碍后均为0(表示不能通过)
确定遍历顺序:由左到右、由上到下
举例推导dp数组
class Solution {
public:
int uniquePathsWithObstacles(vector>& obstacleGrid) {
int m=obstacleGrid.size();
int n=obstacleGrid[0].size();
if(obstacleGrid[0][0]==1||obstacleGrid[m-1][n-1]==1) return 0;
vector> dp(m, vector(n, 0));
//注意特殊条件:如果在终点或起点出现障碍 白搭直接返回
//初始化 第零行第零列是1 表示有一种方法
for(int i=0;i
确定dp数组(dp table)以及下标的含义:对i进行拆分得到的最大乘积dp[i]
确定递推公式:dp[i]=j*dp[i-j]
dp数组如何初始化:dp[0]=0,dp[1]=0没有意义,dp[2]=1
确定遍历顺序:由小到大
举例推导dp数组
class Solution {
public:
int integerBreak(int n) {
vector dp(n+1);//从0~n个数 有n+1
//初始化
dp[0]=0;dp[1]=0;dp[2]=1;
for(int i=3;i<=n;i++){//从3开始
for(int j=1;j<=i/2;j++){
dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]));//j*(i-j)两个数 j*dp[i-j]三个以及以上的数
}
}
return dp[n];
}
};
*比较好理解,但是感觉比上面的题目难了不少
确定dp数组(dp table)以及下标的含义:有几种不同的二叉搜索树
确定递推公式:dp[i]+=dp[j-1]*dp[i-j] j为1~i的所有情况
dp数组如何初始化:dp[0]=1(有一棵树)dp[1]=1(有一棵树)
确定遍历顺序:由小到大
举例推导dp数组
class Solution {
public:
int numTrees(int n) {
vector dp(n+1);//0~n一共n+1个数
dp[0]=1;//初始化 分别只有一棵树
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i]+=dp[j-1]*dp[i-j]; //j-1左子树的个数 i-j是右子树的个数
}
}
return dp[n];
}
};
**对代码仍有不理解的地方,有时间再看一遍讲解
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
确定dp数组(dp table)以及下标的含义:dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少
确定递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); (不放物品i和放物品i)
dp数组如何初始化:背包容量j为0的话,即dp[i][0]=0;dp[0][j]即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值:①当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小;②j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品;其他下标初始为什么数值都可以,因为都会被覆盖
确定遍历顺序:先遍历 物品还是先遍历背包重量都可以
举例推导dp数组
#include
#include
using namespace std;
void test01bagproblem(){
vector weight = {1, 3, 4};
vector value = {15, 20, 30};
int bagweight = 4;
// 二维dp数组
vector> dp(weight.size(), vector(bagweight + 1, 0));
//初始化 行
for(int j=0;j<=bagweight;j++){
if(weight[0]>j) dp[0][j]=0;
else dp[0][j]=value[0];
}
// weight数组的大小 就是物品个数
for(int i = 1; i < weight.size(); i++) { // 遍历物品
for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
cout << dp[weight.size() - 1][bagweight] << endl;
}
int main(){
test01bagproblem();
}
将二维数组变为一维数组
确定dp数组(dp table)以及下标的含义:dp[j] 容量为j的背包,所背的物品价值可以最大为dp[j]。
确定递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); (不放物品i和放物品i)
dp数组如何初始化:dp[0=0,因为背包容量为0所背的物品的最大价值就是0。其余初始化为0
确定遍历顺序:背包从大到小、先遍历物品后遍历背包顺序不可以变动
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
举例推导dp数组
#include
#include
using namespace std;
void test_1_wei_bag_problem() {
vector weight = {1, 3, 4};
vector value = {15, 20, 30};
int bagWeight = 4;
// 初始化
vector dp(bagWeight + 1, 0);
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
cout << dp[bagWeight] << endl;
}
int main() {
test_1_wei_bag_problem();
}
确定dp数组(dp table)以及下标的含义:背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]。当 dp[target] == target 的时候,背包就装满了。
确定递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
dp数组如何初始化:均初始化为0
确定遍历顺序
for(int i = 0; i < nums.size(); i++) {
for(int j = target; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
}
}
举例推导dp数组
class Solution {
public:
bool canPartition(vector& nums) {
int sum=0;
for(int i=0;i dp(10001,0);
// 开始 01背包
for(int i=0;i=nums[i];j--){
dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
}
}
// 集合中的元素正好可以凑成总和target
if(dp[target]==target) return true;
return false;
}
};
确定dp数组(dp table)以及下标的含义:背量为j的背包最大的重量为dp[j]
确定递推公式:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
dp数组如何初始化:均为0
确定遍历顺序
for (int i = 0; i < stones.size(); i++) { // 遍历物品
for (int j = target; j >= stones[i]; j--) { // 遍历背包
dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
举例推导dp数组
class Solution {
public:
int lastStoneWeightII(vector& stones) {
int sum=0;
vector dp(15000,0);//初始化
for(int i=0;i=stones[i];j--){
dp[j]=max(dp[j],dp[j-stones[i]]+stones[i]);
}
}
return sum-dp[target]-dp[target]; //sum-dp[target]是剩余的部分 差值是最后的重量
}
};
left组合 - right组合 = target
left + right = sum
left - (sum - left) = target 推导出 left = (target + sum)/2
target固定,sum固定,left可以求出来
问题就是在集合nums中找出和为left的组合
确定dp数组(dp table)以及下标的含义:填满j(包括j)这么大容积的包,有dp[j]种方法
确定递推公式:
dp[j] += dp[j - nums[i]]
dp数组如何初始化:dp[0]=1,其余均初始化为0
确定遍历顺序
for(int i=0;i=nums[i];j--){
dp[j]+=dp[j-nums[i]];
}
}
举例推导dp数组
class Solution {
public:
int findTargetSumWays(vector& nums, int target) {
int sum=0;
for(int i=0;isum) return 0;//S的绝对值已经大于sum
if((target+sum)%2==1) return 0;//sum 是5,S是2的话其实就是无解
int bagsize=(sum+target)/2;
vector dp(bagsize+1,0);//初始化为0
dp[0]=1;//答案为0的时候有一种方法
//动态规划求解的情况
for(int i=0;i=nums[i];j--){
dp[j]+=dp[j-nums[i]];
}
}
return dp[bagsize];
}
};
*有不明白的地方
m 和 n相当于是一个背包,两个维度的背包
确定dp数组(dp table)以及下标的含义:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。最终结果是dp[m][n]
确定递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
dp数组如何初始化:初始化为0
确定遍历顺序:一定是外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历
举例推导dp数组
class Solution {
public:
int findMaxForm(vector& strs, int m, int n) {
vector> dp(m + 1, vector (n + 1, 0)); // 默认初始化0
for(string str:strs){//遍历物品
int x=0;int y=0;
for(char c:str){
if(c=='0') x++;
else y++;
}
//遍历背包
for(int i=m;i>=x;i--){
for(int j=n;j>=y;j--){
dp[i][j]=max(dp[i][j], dp[i - x][j - y] + 1);
}
}
}
return dp[m][n];
}
};
*有不明白的地方
完全背包又是也是01背包稍作变化而来,即:完全背包的物品数量是无限的。
有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。
完全背包的物品是可以添加多次的,所以要从小到大去遍历
物品在外层循环,遍历背包容量在内层循环
// 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = weight[i]; j <= bagWeight ; j++) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
void test_CompletePack() {
vector weight = {1, 3, 4};
vector value = {15, 20, 30};
int bagWeight = 4;
vector dp(bagWeight + 1, 0);
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = weight[i]; j <= bagWeight; j++) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
cout << dp[bagWeight] << endl;
}
int main() {
test_CompletePack();
}
钱币数量不限,就知道这是一个完全背包
组合数:组合不强调元素之间的顺序,排列强调元素之间的顺序
确定dp数组(dp table)以及下标的含义:装满容量为j的背包有dp[j]种方法
确定递推公式:dp[j] += dp[j - coins[i]](求装满背包有几种方法,公式都是:dp[j] += dp[j - nums[i]])
dp数组如何初始化:dp[0] = 1,其余初始化为0
确定遍历顺序
for (int i = 0; i < coins.size(); i++) { // 遍历物品
for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量
dp[j] += dp[j - coins[i]];
}
}
举例推导dp数组
class Solution {
public:
int change(int amount, vector& coins) {
vector dp(amount+1,0);//初始化
dp[0]=1;
for(int i=0;i
组合数:外层for循环遍历物品,内层for遍历背包。
排列数:外层for遍历背包,内层for循环遍历物品。
排列数:排列强调顺序,(1,5)和(5,1)是两个不同的排列
确定dp数组(dp table)以及下标的含义:装满容量为j的背包有dp[j]种方法
确定递推公式:dp[j] += dp[j - coins[i]](求装满背包有几种方法,公式都是:dp[j] += dp[j - nums[i]])
dp数组如何初始化:dp[0] = 1,其余初始化为0
确定遍历顺序:先遍历背包再遍历物品
举例推导dp数组
class Solution {
public:
int combinationSum4(vector& nums, int target) {
vector dp(target+1,0);//初始化
dp[0]=1;
for(int j=0;j<=target;j++){
for(int i=0;i=0&&dp[j] < INT_MAX - dp[j - nums[i]]) //C++测试用例有两个数相加超过int的数据,所以需要在if里加上dp[i] < INT_MAX - dp[i - num]
dp[j]+=dp[j-nums[i]];
}
}
return dp[target];
}
};
确定dp数组(dp table)以及下标的含义:凑足总额为j所需钱币的最少个数为dp[j]
确定递推公式:dp[j] =min(dp[j - coins[i]] + 1, dp[j]);
dp数组如何初始化:凑足总金额为0所需钱币的个数一定是0,那么dp[0] = 0;因为要取得都是最小值,所以初始化为int的最大值
确定遍历顺序:不强调集合是组合还是排列
举例推导dp数组
class Solution {
public:
int coinChange(vector& coins, int amount) {
vector dp(amount+1,INT_MAX);
dp[0]=0;
for(int i=0;i=0&&dp[j-coins[i]]!=INT_MAX)
dp[j]=min(dp[j],dp[j-coins[i]]+1);
}
}
if(dp[amount]==INT_MAX) return -1;
return dp[amount];
}
};
确定dp数组(dp table)以及下标的含义:和为j的完全平方数的最少数量为dp[j]
确定递推公式:dp[j] = min(dp[j - i * i] + 1, dp[j]);
dp数组如何初始化:dp[0]=0,其余均为最大值这样再取最小值的时候才不会覆盖
确定遍历顺序:不强调集合是组合还是排列
举例推导dp数组
class Solution {
public:
int numSquares(int n) {
vector dp(n+1,INT_MAX);
dp[0]=0;//初始化
for(int i=0;i*i<=n;i++){//遍历物品
for(int j=i*i;j<=n;j++){//遍历背包
if(j-i*i>=0&&dp[j-i*i]!=INT_MAX)
dp[j]=min(dp[j],dp[j-i*i]+1);
}
}
if(dp[n]==INT_MAX) return -1;
return dp[n];
}
};
确定dp数组(dp table)以及下标的含义:字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词
确定递推公式:如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。(j < i )
dp数组如何初始化:dp[0]=true
确定遍历顺序:这里是求排列数,有一定的顺序要求,需要先遍历背包再遍历物品。
举例推导dp数组
class Solution {
public:
bool wordBreak(string s, vector& wordDict) {
unordered_set wordSet(wordDict.begin(), wordDict.end());
vector dp(s.size() + 1, false);
dp[0] = true;
for(int i=1;i<=s.size();i++){
//遍历背包
for(int j=0;j
有N种物品和一个容量为V 的背包。第i种物品最多有Mi件可用,每件耗费的空间是Ci ,价值是Wi 。求解将哪些物品装入背包可使这些物品的耗费的空间 总和不超过背包容量,且价值总和最大。
void test_multi_pack() {
vector weight = {1, 3, 4};
vector value = {15, 20, 30};
vector nums = {2, 3, 2};
int bagWeight = 10;
for (int i = 0; i < nums.size(); i++) {
while (nums[i] > 1) { // nums[i]保留到1,把其他物品都展开
weight.push_back(weight[i]);
value.push_back(value[i]);
nums[i]--;
}
}
vector dp(bagWeight + 1, 0);
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
for (int j = 0; j <= bagWeight; j++) {
cout << dp[j] << " ";
}
cout << endl;
}
cout << dp[bagWeight] << endl;
}
int main() {
test_multi_pack();
}
问能否能装满背包(或者最多装多少):dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
问装满背包有几种方法:dp[j] += dp[j - nums[i]]
问背包装满最大价值:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
问装满背包所有物品的最小个数:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);
二维dp数组01背包先遍历物品还是先遍历背包都是可以的,且第二层for循环是从小到大遍历。
一维dp数组01背包只能先遍历物品再遍历背包容量,且第二层for循环是从大到小遍历
纯完全背包的一维dp数组实现,先遍历物品还是先遍历背包都是可以的,且第二层for循环是从小到大遍历
求组合数就是外层for循环遍历物品,内层for遍历背包
求排列数就是外层for遍历背包,内层for循环遍历物品
确定dp数组(dp table)以及下标的含义:考虑下标i,偷的最大的金币数为dp[i],答案是dp[nums.size()-1]
确定递推公式:dp[i]=max(dp[i-1],dp[i-2]+nums[i])
dp数组如何初始化:dp[0]=nums[0] dp[1]=max(nums[0],nums[1]),其余下标
确定遍历顺序:顺序遍历;for(i=2;i
举例推导dp数组
class Solution {
public:
int rob(vector& nums) {
if(nums.size()==0) return 0;
if(nums.size()==1) return nums[0];
vector dp(nums.size());
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);//取大的值
for(int i=2;i
区别:连城环了 首尾是挨着的
三种情况:①首尾都不选②选首元素③选尾元素==》取②③的最大值
确定dp数组(dp table)以及下标的含义:偷的最大的金币数为dp[i],答案是dp[nums.size()-1]
确定递推公式:dp[i]=max(dp[i-1],dp[i-2]+nums[i])
dp数组如何初始化:dp[0]=nums[0] dp[1]=max(nums[0],nums[1]),其余下标
确定遍历顺序:顺序遍历;for(i=2;i
举例推导dp数组
class Solution {
public:
int rob1(vector& nums,int start,int end) {
vector dp(nums.size());
if(start==end) return nums[start];
dp[start]=nums[start];
dp[start+1]=max(nums[start],nums[start+1]);//取大的值
for(int i=start+2;i<=end;i++){
dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[end];
}
int rob(vector& nums) {
//!!第一个房屋和最后一个房屋是紧挨着的
//分为两种情况:①包含首元素 ②不包含首元素包含尾元素
if(nums.size()==0) return 0;
if(nums.size()==1) return nums[0];
int result1=rob1(nums,0,nums.size()-2);
int result2=rob1(nums,1,nums.size()-1);
return max(result1,result2);
}
};
区别:是二叉树形式
确定dp数组(dp table)以及下标的含义:下标为0记录不偷该节点所得到的的最大金钱,下标为1记录偷该节点所得到的的最大金钱。
确定递推公式:偷当前节点,那么左右孩子就不能偷,val1 = cur->val + left[0] + right[0]; 不偷当前节点,那么左右孩子就可以偷,至于到底偷不偷一定是选一个最大的,所以:val2 = max(left[0], left[1]) + max(right[0], right[1]);最后当前节点的状态就是{val2, val1}; 即:{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱}
dp数组如何初始化:确定终止条件在遍历的过程中,如果遇到空节点的话,很明显,无论偷还是不偷都是0,所以就返回
确定遍历顺序:后序遍历。 因为要通过递归函数的返回值来做下一步计算。通过递归左节点,得到左节点偷与不偷的金钱。通过递归右节点,得到右节点偷与不偷的金钱。
举例推导dp数组
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector robTree(TreeNode *cur){
//终止条件
if(cur==NULL) return vector {0,0};
//遍历顺序 做 右 中的后序遍历
//递归左节点
vector leftdp=robTree(cur->left);
//递归右结点
vector rightdp=robTree(cur->right);
//单层递归逻辑
//偷当前结点 左右孩子不偷
int val1=cur->val+leftdp[0]+rightdp[0];
//不偷当前结点 判断左右孩子大小 偷大的
int val2=max(leftdp[0],leftdp[1])+max(rightdp[0],rightdp[1]);
return vector {val2,val1};
}
//主函数
int rob(TreeNode* root) {
vector result=robTree(root);
return max(result[0],result[1]);
}
};
确定dp数组(dp table)以及下标的含义:dp[i][0]持有股票的最大现金,dp[i][0]不持有股票的最大现金;结果:max(dp[size-1][0],dp[size-1][1]) **持有表示已经有,不一定是当天才买这个股票
递推公式:dp[i][0]=max(-dp[i-1][0] -price[i]);dp[i][1]=max(dp[i-1][1],dp[i-1][0]+price[i])
dp数组如何初始化:dp[0][0]=-price[0];dp[0][1]=0
确定遍历顺序:从前往后遍历---for(i=1;i
举例推导dp数组
class Solution {
public:
int maxProfit(vector& prices) {
int a=prices[0];
vector> dp(prices.size(), vector(2));
dp[0][0] -= prices[0];//持有股票
dp[0][1]=0;//不持有股票
for(int i=1;i
一只股票可以买卖多次,所以当第i天买入股票的时候,所持有的现金可能有之前买卖过的利润
确定dp数组(dp table)以及下标的含义:dp[i][0] 表示第i天持有股票所得现金。dp[i][1] 表示第i天不持有股票所得最多现金
递推公式:dp[i][0]=max(-dp[i-1][0],dp[i-1][1]-price[i]);dp[i][1]=max(dp[i-1][1],dp[i-1][0]+price[i])
dp数组如何初始化:
确定遍历顺序:
举例推导dp数组
class Solution {
public:
int maxProfit(vector& prices) {
int len=prices.size();
vector> dp(len,vector(2,0));
dp[0][0]=-prices[0];
dp[0][1]=0;
for(int i=1;i
至多买卖两次,那么是买一次还是买两次呢??
确定dp数组(dp table)以及下标的含义:dp[i][0] 不操作;dp[i][1]第一次持有;dp[i][2]第一次不持有;dp[i][3]第二次持有;dp[i][4]第二次不持有
递推公式:
dp[i][0]=dp[i-1][0]; 就相当于是0
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-price[i]);
dp[i][2]=max(dp[i-1][2],dp[i-1][1]+price[i]);
dp[i][3]=max(dp[i-1][3],dp[i-1][2]-price[i]);
dp[i][4]=max(dp[i-1][4],dp[i-1][3]+price[i]);
dp数组如何初始化:dp[0][0]=0 dp[0][1]=-price[0] dp[0][2]=0 dp[0][3]=-price[0] dp[0][4]=0
确定遍历顺序:for(i=1;i
举例推导dp数组
class Solution {
public:
int maxProfit(vector& prices) {
if(prices.size()==0) return 0;
vector> dp(prices.size(),vector(5,0));
dp[0][0]=0;//不持有
dp[0][1]=-prices[0];//第一次持有
dp[0][2]=0;//第一次不持有
dp[0][3]=-prices[0];//第二次持有
dp[0][4]=0;//第二次不持有
for(int j=1;j
买卖股票k次,第一个维度是股票第二维度是次数2k
确定dp数组(dp table)以及下标的含义:dp[i][j] i:第几天 j:第几次买卖
递推公式:
dp[i][0]=dp[i-1][0]; 就相当于是0
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-price[i]); //第一次买
dp[i][2]=max(dp[i-1][2],dp[i-1][1]+price[i]);//第一次卖
dp[i][3]=max(dp[i-1][3],dp[i-1][2]-price[i]); //第二次买
dp[i][4]=max(dp[i-1][4],dp[i-1][3]+price[i]); //第二次卖
dp数组如何初始化:for(j=1;j<2*k;j+=2){dp[0][j]=-price[0]}
确定遍历顺序:for(j=0;j<2*k;j+=2){dp[i][j+1]=max(dp[i-1][j+1],dp[i-1][j]-price[i]);dp[i][j+2]=max(dp[i-1][j+2],dp[i][j+1]+price[i])}
举例推导dp数组
class Solution {
public:
int maxProfit(int k, vector& prices) {
if(prices.size()==0) return 0;
vector> dp(prices.size(),vector(2*k+1,0));
//初始化过程
for(int j=1;j<2*k;j+=2){
dp[0][j]=-prices[0];
}
//递推公式
for(int j=0;j<2*k;j+=2){
for(int i=1;i
确定dp数组(dp table)以及下标的含义:dp[i][0]持股;dp[i][1]保持卖出股票的状态;dp[i][2] 真正卖出股票的状态;dp[i][3]冷冻期
递推公式:
dp[i][0]=max(dp[i-1][0]//持有 dp[i-1][3]-price[i],dp[i-1][1]//买入股票 前一天是冷冻期or前一天是保持卖出股票状态)
dp[i][1]=dp[i-1][1]//保持卖出股票状态dp[i-1][3]//冷冻期下一天
dp[i][2]=dp[i-1][1]+price[i]//前一天持有股票
dp[i][3]=dp[i-1][2]//前一天卖出股票,不是保持状态
dp数组如何初始化:dp[0][0]=-price[0] dp[0][1]=0 dp[0][2]=0 dp[0][3]=0
确定遍历顺序:顺序遍历 i=1 ==》max(dp[pricesize()-1][1,2,3])
举例推导dp数组
class Solution {
public:
int maxProfit(vector& prices) {
if(prices.size()==0) return 0;
int len=prices.size();
vector> dp(len,vector(4,0));
dp[0][0]=-prices[0];//持股中
for(int i=1;i
确定dp数组(dp table)以及下标的含义:dp[i][0]持有股最大现金;dp[i][1]不持有股票的最大先进
递推公式:
dp[i][0]=max(dp[i-1][0],dp[i-1][1]-price[i])
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+price[i]-fee)
dp数组如何初始化:dp[0][0]=-price[0] dp[0][1]=0
确定遍历顺序:顺序遍历 i=1 ==》max(dp[pricesize()-1][1,2,3])
举例推导dp数组
class Solution {
public:
int maxProfit(vector& prices, int fee) {
int n=prices.size();
vector> dp(n,vector(2,0));
dp[0][0]=-prices[0];
dp[0][1]=0;
for(int i=1;i
确定dp数组(dp table)以及下标的含义:dp[i]以nums[i]为结尾的最长递增子序列的长度
确定递推公式:dp[i]=max(dp[i],dp[j]+1) (if(nums[i]
dp数组如何初始化:dp[i]=1;(因为至少有一个)
确定遍历顺序:i从小到大遍历;内层循环j从小到大遍历
举例推导dp数组
class Solution {
public:
int lengthOfLIS(vector& nums) {
//定义dp数组,初始化为1,因为至少会有长度为1的解
vector dp(nums.size(),1);
//递推公式,如果后面的比前面的大,就让长度+1
for(int i=1;i
确定dp数组(dp table)以及下标的含义:dp[i][j]以0~i-1为结尾的nums1,以0~j-1为结尾的nums2的最长公共子序列的长度
确定递推公式:dp[i]=dp[i-1][j-1]+1 (if(nums1[i-1]
dp数组如何初始化:dp[i][0]和dp[0][j]=0,其余均初始为0
确定遍历顺序:从1开始,哪个先遍历无所谓,从小到大遍历,小于等于nums1.size()
举例推导dp数组
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
//定义二位dp数组,初始化为0
vector> dp(text1.size()+1,vector(text2.size()+1,0));
int result=0;//存储最终结果
//循环打印dp数组
for(int i=1;i<=text1.size();i++){
for(int j=1;j<=text2.size();j++){
if(text1[i-1]==text2[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
}
else{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
result=max(result,dp[i][j]);
}
}
return result;
}
};
如果找到最长公共子序列的话,他们的线就不会相交,因为保证了相同的顺序
确定dp数组(dp table)以及下标的含义:dp[i][j]以0~i-1为结尾的nums1,以0~j-1为结尾的nums2的最长公共子序列的长度
确定递推公式:dp[i]=dp[i-1][j-1]+1 (if(nums1[i-1]
dp数组如何初始化:dp[i][0]和dp[0][j]=0,其余均初始为0
确定遍历顺序:从1开始,哪个先遍历无所谓,从小到大遍历,小于等于nums1.size()
举例推导dp数组
class Solution {
public:
int maxUncrossedLines(vector& nums1, vector& nums2) {
vector> dp(nums1.size()+1,vector(nums2.size()+1,0));
int result=0;
for(int i=1;i<=nums1.size();i++){
for(int j=1;j<=nums2.size();j++){
if(nums1[i-1]==nums2[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
}
else{
dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
}
result=max(result,dp[i][j]);
}
}
return result;
}
};
确定dp数组(dp table)以及下标的含义:dp[i]以nums[i]为结尾的最长递增子序列的长度
确定递推公式:dp[i]=max(dp[i],dp[i]+1) (if(nums[i-1]
dp数组如何初始化:dp[i]=1;(因为至少有一个)
确定遍历顺序:i从小到大遍历
举例推导dp数组
class Solution {
public:
int findLengthOfLCIS(vector& nums) {
//初始化dp数组均为1
vector dp(nums.size(),1);
//地推公式
for(int i=1;i
确定dp数组(dp table)以及下标的含义:dp[i][j]以i-1为结尾的nums1,以j-1为结尾的nums2的最长公共子序列的长度
确定递推公式:dp[i]=dp[i-1][j-1]+1 (if(nums1[i-1]
dp数组如何初始化:dp[i][0]和dp[0][j]=0,其余均初始为0
确定遍历顺序:从1开始,哪个先遍历无所谓,从小到大遍历,小于等于nums1.size()
举例推导dp数组
class Solution {
public:
int findLength(vector& nums1, vector& nums2) {
//定义二位dp数组,初始化为0
vector> dp(nums1.size()+1,vector(nums2.size()+1,0));
int result=0;//存储最终结果
//循环打印dp数组
for(int i=1;i<=nums1.size();i++){
for(int j=1;j<=nums2.size();j++){
if(nums1[i-1]==nums2[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
}
result=max(result,dp[i][j]);
}
}
return result;
}
};
确定dp数组(dp table)以及下标的含义:dp[i]以i为结尾(nums[i])的最长子序列和
确定递推公式:dp[i]=max(dp[i-1]+nums[i],nums[i])
dp数组如何初始化:初始为0
确定遍历顺序:从1开始,从小到大遍历
举例推导dp数组
class Solution {
public:
int maxSubArray(vector& nums) {
//前提条件
if(nums.size()==0) return 0;
if(nums.size()==1) return nums[0];
//初始化数组为0
vector dp(nums.size(),0);
dp[0]=nums[0];
//递归dp数组
for(int i=1;i
确定dp数组(dp table)以及下标的含义:dp[i][j]以i-1为结尾的字符串s和以j-1为结尾的字符串t的相同子序列的长度和
确定递推公式:if(s[i-1]==t[j-1]) dp[i][j]=dp[i-1][j-1]+1 else dp[i][j]=dp[i][j-1]
dp数组如何初始化:dp[i][0]=0 dp[0][j]=0 均初始化为0即可
确定遍历顺序:从左到右从上到下 for(i=1;i<=s.size;i++){for(j=1;j<=t.size;j++){ }}
结果dp[s.size][t.size]==s.size 相等
举例推导dp数组
class Solution {
public:
bool isSubsequence(string s, string t) {
vector> dp(s.size()+1,vector(t.size()+1,0));
for(int i=1;i<=s.size();i++){
for(int j=1;j<=t.size();j++){
if(s[i-1]==t[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
}
else{
dp[i][j]=dp[i][j-1];
}
}
}
if(dp[s.size()][t.size()]==s.size()) return true;
else return false;
}
};
确定dp数组(dp table)以及下标的含义:dp[i][j]以i-1为结尾的字符串s中有以j-1为结尾的t的个数为dp[i][j]
确定递推公式:if(s[i-1]==t[j-1]) dp[i][j]=dp[i-1][j-1]+dp[i-1][j] else dp[i][j]=dp[i-1][j]
dp数组如何初始化:dp[i][0]=1 dp[0][j]=0 dp[0][0]=1 均初始化为0即可
确定遍历顺序:从左到右从上到下 for(i=1;i<=s.size;i++){for(j=1;j<=t.size;j++){ }}
结果dp[s.size][t.size]
举例推导dp数组
class Solution {
public:
int numDistinct(string s, string t) {
vector> dp(s.size()+1,vector(t.size()+1));
dp[0][0]=1;
for(int i=1;i
确定dp数组(dp table)以及下标的含义:dp[i][j]以i-1为结尾的字符串word1和以j-1为结尾的字符串word2为相同的最小操作次数为dp[i][j]
确定递推公式:if(word1[i-1]==word2[j-1]) dp[i][j]=dp[i-1][j-1] else dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+2)
dp数组如何初始化:dp[0][j]=j dp[i][0]=i 均初始化为0即可
确定遍历顺序:从左到右从上到下 for(i=1;i<=s.size;i++){for(j=1;j<=t.size;j++){ }}
结果dp[word1.size()][word2.size()]
举例推导dp数组
class Solution {
public:
int minDistance(string word1, string word2) {
//确定dp数组
vector> dp(word1.size() + 1, vector(word2.size() + 1));
//初始化
for(int i=0;i<=word1.size();i++){
dp[i][0]=i;
}
for(int j=0;j<=word2.size();j++){
dp[0][j]=j;
}
//递推公式 遍历顺序
for (int i = 1; i <= word1.size(); i++) {
for (int j = 1; j <= word2.size(); j++) {
if (word1[i - 1] == word2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
}
}
}
//得出结果
return dp[word1.size()][word2.size()];
}
};
确定dp数组(dp table)以及下标的含义:dp[i][j]以i-1为结尾的字符串word1和以j-1为结尾的字符串word2为相同的最少操作次数为dp[i][j]
确定递推公式:if(word1[i-1]==word2[j-1]) dp[i][j]=dp[i-1][j-1] else(不相同的情况有增、删、替换)删除 增加 修改:dp[i][j]=min(dp[i-1][j]+1dp[i][j-1]+1,dp[i-1][j-1]+1)
dp数组如何初始化:dp[0][j]=j dp[i][0]=i 均初始化为0即可
确定遍历顺序:从左到右从上到下 for(i=1;i<=s.size;i++){for(j=1;j<=t.size;j++){ }}
结果dp[word1.size][word2.size]
举例推导dp数组
class Solution {
public:
int minDistance(string word1, string word2) {
vector> dp(word1.size()+1,vector(word2.size()+1));
//初始化
for(int i=0;i<=word1.size();i++){
dp[i][0]=i;
}
for(int j=0;j<=word2.size();j++){
dp[0][j]=j;
}
//确定遍历顺序
for(int i=1;i<=word1.size();i++){
for(int j=1;j<=word2.size();j++){
if(word1[i-1]==word2[j-1]){
dp[i][j]=dp[i-1][j-1];
}
else{
//对字母 删除 增加 修改
dp[i][j]=min(min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+1);
}
}
}
return dp[word1.size()][word2.size()];
}
};
单独一个元素一定是回文子串 回文串左右两边的元素是相同对称的
确定dp数组(dp table)以及下标的含义:dp[i][j] [i,j]子串是否是回文子串 true/false
确定递推公式:if(s[i]==s[j]){if(j-i<=1){dp[i][j]=true;result++;}else if(dp[i+1][j-1]==true){dp[i][j]=true;result++}}dp[i][j]=false i==j a;j-i=1 aa;j-i>1一定是大于2的子串 判断i+1和j-1之间是不是回文子串即dp[i+1][j-1]==true
dp数组如何初始化:false
确定遍历顺序:从左到右从下到上 for(i=s.szie-1;i>=0i--){for(j=i;j<=s.size;j++){递推公式 }}
举例推导dp数组
class Solution {
public:
int countSubstrings(string s) {
vector> dp(s.size(),vector(s.size(),false));
int result=0;
for(int i=s.size()-1;i>=0;i--){
for(int j=i;j
子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
确定dp数组(dp table)以及下标的含义:dp[i][j] [i,j]内的回文子序列长度为dp[i][j]
确定递推公式:if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1]+2(相同) else dp[i][j]=max(dp[i][j-1],dp[i+1][j])(不相同)
dp数组如何初始化:dp[i][i]=1(i,j相同的情况下初始化为1) 其余下标默认初始化为0
确定遍历顺序:从左到右从下到上 for(i=s.szie-1;i>=0i--){for(j=i+1;j<=s.size;j++){递推公式 }}
举例推导dp数组
class Solution {
public:
int longestPalindromeSubseq(string s) {
vector> dp(s.size(),vector(s.size(),0));
for(int i=0;i=0;i--){
for(int j=i+1;j
历时将近一个月,中间经历了大大小小的事,终于完成了