您的位置:首页 > 文旅 > 旅游 > 昆明设计公司排行榜_自助建站之星_广告电话_上海app网络推广公司电话

昆明设计公司排行榜_自助建站之星_广告电话_上海app网络推广公司电话

2024/12/23 4:46:52 来源:https://blog.csdn.net/m0_74795952/article/details/143616316  浏览:    关键词:昆明设计公司排行榜_自助建站之星_广告电话_上海app网络推广公司电话
昆明设计公司排行榜_自助建站之星_广告电话_上海app网络推广公司电话

Day40 | 动态规划 :完全背包应用 组合总和IV(类比爬楼梯)

动态规划应该如何学习?-CSDN博客

01背包模板 | 学习总结-CSDN博客

完全背包模板总结-CSDN博客

难点:

代码都不难写,如何想到完全背包并把具体问题抽象为完全背包才是关键

文章目录

  • Day40 | 动态规划 :完全背包应用 组合总和IV(类比爬楼梯)
    • 377.组合总和IV
      • 思路分析:
      • 难点讲解
        • 1.为什么这样可以求出来排列数量?
        • 2.dp数组如何初始化,我们应该如何理解初始化
      • 1.动态规划
      • 2.回溯法
      • 3.记忆化搜索
    • 到这里我们会发现,这里的dp不就是先遍历背包容量后遍历物品的完全背包吗?
      • 组合
      • 排列

377.组合总和IV

377. 组合总和 Ⅳ - 力扣(LeetCode)

思路分析:

虽然说是组合,但本质是求的排列,要考虑元素的顺序

代码随想录只是说了一下遍历顺序不同,可以分别求出排列数量和组合数量,但大家肯定还是不太清楚。还是看看灵神的题解怎么说吧。

本题其实就是 70. 爬楼梯,我们每次从 nums 中选一个数,作为往上爬的台阶数,问爬 target 个台阶有多少种方案。70 那题可以看作 nums=[1,2],因为每次只能爬 1 个或 2 个台阶。

dp[i]的含义就是爬上第i个台阶的方案数量

1.在那道题中

我们的代码是

dp[i]=dp[i-1]+dp[i-2]

2.如果说我们一次可以爬k个台阶,当然k要比target(要爬的总楼梯数量)小

dp[i]=dp[i-1]+dp[i-2]+dp[i-3]+....+dp[i-k]
//等价于
for(int j=1;j<=k;j++)dp[i]+=dp[i-j];

3.如果说我们一次可以爬的台阶数量是nums数组里面的,那么j就是nums[j],上面的1-k就相当于这里的nums数组的遍历

dp[i]=dp[i-nums[1]]+dp[i-nums[2]]+dp[i-nums[3]]+....+dp[i-nums[nums.size()-1]]
//等价于
for(int j=0;j<nums.size();j++)dp[i]+=dp[i-nums[j]];

相当于,在第一种情况中

nums数组为{1,2}

在第二种情况中

nums数组为{1,2,3,…,k}

难点讲解

1.为什么这样可以求出来排列数量?

举个例子,我们要登上台阶3(target=3)

我们的nums数组为{1,2}

那很显然我们dp[3]=dp[1]+dp[2]了

就是从第一个台阶一次爬2个(1,2先爬一个再爬两个)

和从第二个台阶一次爬1个(2,1先爬两个再爬一个)

很明显,可以是排列的原因就是,我们在爬每一个台阶往上爬看能不能看target的时候会从nums的数组的第一个元素开始重新遍历,会把所有的元素重新遍历一遍

2.dp数组如何初始化,我们应该如何理解初始化
dp[0]=1;

要知道这道题是达到target就行

假如我们nums里面有一个值就是target

那岂不是直接就会有一种方案是从0到target

而我们的递推公式里面有这个情况

dp[target]=…+dp[target-target](这个值就是dp[0])

这个方案数量为1,除此之外呢不会有其他的情况可以取到这个值

这个初始化和下面代码等价

for(int i=0;i<nums.size();i++)if(nums[i]<=target)dp[nums[i]]=1;

为什么是等价的呢?

如果dp[0]没有初始化为1

那dp[nums[i]]=…+dp[nums[i]-nums[i]]里面的最后一项就是dp[0]=0,你发现就少加了一个1

而我们初始化的时候上面的这三行代码已经把它初始化成1了,dp[0]=0也就无所谓了

所以两种初始化方式都可以

而用回溯法递归的话,dp[0]=1可以理解为是我们从target开始向下爬,可以爬到0,那就是找到了一个合法的方案,我们就会返回1

1.动态规划

既然递推公式和初始化全想清楚了,那就直接上动态规划,完了再想回溯和记忆化搜索

1.确定dp数组以及下标的含义

dp[i]: 凑成目标正整数为i的排列个数为dp[i]

也就是上面说的爬到第i个台阶有多少种方法

2.确定递推公式

正如上面爬楼梯所说的

for(int j=0;j<nums.size();j++)dp[i]+=dp[i-nums[j]];

3.dp数组如何初始化

请看难点讲解2

4.确定遍历顺序

外层循环遍历台阶i

内层循环nums数组

都是从前往后遍历

完整代码:

啊,i>=nums[j]是因为你一次爬(nums[j])的总不能比要爬的总数(i)高了吧

class Solution {
public:int combinationSum4(vector<int>& nums, int target) {vector<unsigned> dp(target+1,0);/*for(int i=0;i<nums.size();i++)if(nums[i]<=target)dp[nums[i]]=1;*/dp[0]=1;for(int i=1;i<=target;i++)for(int j=0;j<nums.size();j++)if(i>=nums[j])dp[i]+=dp[i-nums[j]];return dp[target];}
};

2.回溯法

我们从第c个台阶往第0个台阶走

能到达第0个台阶那就是找到了一个合法的方案

到不了就是没找到一个合法的方案

1.参数和返回值

c是要爬的第几个台阶

nums是我们一次可以爬几个台阶

dfs返回的就是我们爬到c可以有几个方案

int dfs(int c,vector<int>& nums)

2.终止条件

如果c==0,说明我们可以从target向下走到底

说明找到了一个合法的方法,返回1

if(c==0)return 1;

3.本层逻辑

动态规划两层for循环,那么在递归函数里面就需要一个for循环,另外一层循环由递归体现

还是一样的,爬到c的方法就是,从c-num[0],c-nums[1]…c-nums[i]这些台阶爬到c的方案数量相加

		int res=0;for(int i=0;i<nums.size();i++)if(c>=nums[i])res+=dfs(c-nums[i],nums);return res;

完整代码:

当然是超时的

class Solution {
public:int dfs(int c,vector<int>& nums){if(c==0)return 1;int res=0;for(int i=0;i<nums.size();i++)if(c>=nums[i])res+=dfs(c-nums[i],nums);return res;}int combinationSum4(vector<int>& nums, int target) {return dfs(target,nums);}
};

3.记忆化搜索

就是还是全都初始化为-1,每次返回前给dp赋值,碰到不是-1的那就是算过的,那就直接返回计算过的结果,不需要再次递归了

完整代码:

class Solution {
public:int dfs(int c,vector<int>& nums,vector<unsigned>& dp){if(c==0)return 1;int res=0;if(dp[c]!=-1)return dp[c];for(int i=0;i<nums.size();i++)if(c>=nums[i])res+=dfs(c-nums[i],nums,dp);return dp[c]=res;}int combinationSum4(vector<int>& nums, int target) {vector<unsigned> dp(target+1,-1);return dfs(target,nums,dp);}
};

到这里我们会发现,这里的dp不就是先遍历背包容量后遍历物品的完全背包吗?

所以先遍历物品,后遍历背包容量 得到的就是nums能凑成target的组合

先遍历背包容量,后遍历物品 得到的就是nums能凑成target的排列

组合

经典题目零钱兑换II

Day39 | 动态规划 :完全背包应用 零钱兑换&&零钱兑换II-CSDN博客

排列

本次题解的组合总和IV

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com