mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-05 22:59:31 +08:00
Update
This commit is contained in:
@ -1,145 +0,0 @@
|
|||||||
# 完全背包的排列问题模拟
|
|
||||||
|
|
||||||
#### Problem
|
|
||||||
|
|
||||||
1. 排列问题是完全背包中十分棘手的问题。
|
|
||||||
2. 其在迭代过程中需要先迭代背包容量,再迭代物品个数,使得其在代码理解上较难入手。
|
|
||||||
|
|
||||||
#### Contribution
|
|
||||||
|
|
||||||
本文档以力扣上[组合总和IV](https://leetcode.cn/problems/combination-sum-iv/)为例,提供一个二维dp的代码例子,并提供模拟过程以便于理解
|
|
||||||
|
|
||||||
#### Code
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
int combinationSum4(vector<int>& nums, int target) {
|
|
||||||
// 定义背包容量为target,物品个数为nums.size()的dp数组
|
|
||||||
// dp[i][j]表示将第0-i个物品添加入排列中,和为j的排列方式
|
|
||||||
vector<vector<int>> dp (nums.size(), vector(target+1,0));
|
|
||||||
|
|
||||||
// 表示有0,1,...,n个物品可选择的情况下,和为0的选择方法为1:什么都不取
|
|
||||||
for(int i = 0; i < nums.size(); i++) dp[i][0] = 1;
|
|
||||||
|
|
||||||
// 必须按列遍历,因为右边数组需要知道左边数组最低部的信息(排列问题)
|
|
||||||
// 后面的模拟可以更清楚的表现这么操作的原因
|
|
||||||
for(int i = 1; i <= target; i++){
|
|
||||||
for(int j = 0; j < nums.size(); j++){
|
|
||||||
// 只有nums[j]可以取的情况
|
|
||||||
if(j == 0){
|
|
||||||
if(nums[j] > i) dp[j][i] = 0;
|
|
||||||
// 如果背包容量放不下 那么此时没有排列方式
|
|
||||||
else dp[j][i] = dp[nums.size()-1][i-nums[j]];
|
|
||||||
// 如果背包容量放的下 全排列方式为dp[最底层][容量-该物品容量]排列方式后面放一个nums[j]
|
|
||||||
}
|
|
||||||
// 有多个nums数可以取
|
|
||||||
else{
|
|
||||||
// 如果背包容量放不下 那么沿用0-j-1个物品的排列方式
|
|
||||||
if(nums[j] > i) dp[j][i] = dp[j-1][i];
|
|
||||||
// 如果背包容量放得下 在dp[最底层][容量-该物品容量]排列方式后面放一个nums[j]后面放个nums[j]
|
|
||||||
// INT_MAX避免溢出
|
|
||||||
else if(i >= nums[j] && dp[j-1][i] < INT_MAX - dp[nums.size()-1][i-nums[j]])
|
|
||||||
dp[j][i] = dp[j-1][i] + dp[nums.size()-1][i-nums[j]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 打印dp数组
|
|
||||||
for(int i = 0; i < nums.size(); i++){
|
|
||||||
for(int j = 0; j <= target; j++){
|
|
||||||
cout<<dp[i][j]<<" ";
|
|
||||||
}
|
|
||||||
cout<<endl;
|
|
||||||
}
|
|
||||||
return dp[nums.size()-1][target];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Simulation
|
|
||||||
|
|
||||||
##### 样例 nums = [2,3,4], target = 6
|
|
||||||
|
|
||||||
##### 1. 初始化一个3x7的dp数组
|
|
||||||
|
|
||||||
1 0 0 0 0 0 0
|
|
||||||
1 0 0 0 0 0 0
|
|
||||||
1 0 0 0 0 0 0
|
|
||||||
|
|
||||||
dp\[0-2\]\[0\] = 1,含义是有nums[0-2]物品时使得背包容量为0的取法为1,作用是在取到nums[i]物品使得背包容量为nums[i]时取法为1。
|
|
||||||
|
|
||||||
##### 2.迭代方式
|
|
||||||
|
|
||||||
必须列优先,因为右边的数组在迭代时需要最左下的数组最终结果。
|
|
||||||
|
|
||||||
##### 3. 模拟过程
|
|
||||||
|
|
||||||
i = 1, j = 0 dp\[0\]\[1\] = 0,表示在物品集合{2}中无法组成和为1
|
|
||||||
i = 1, j = 1 dp\[1\]\[1\] = 0,表示在物品集合{2,3}中无法组成和为1
|
|
||||||
i = 1, j = 2 dp\[2\]\[1\] = 0,表示在物品集合{2,3,4}中无法组成和为1
|
|
||||||
|
|
||||||
1 0 0 0 0 0 0
|
|
||||||
1 0 0 0 0 0 0
|
|
||||||
1 **0** 0 0 0 0 0
|
|
||||||
|
|
||||||
此时dp\[2\]\[1\]作为第1列最底部的元素,表示所有物品都有的情况下组成和为1的排列方式为0
|
|
||||||
|
|
||||||
————————————————————————————
|
|
||||||
|
|
||||||
i = 2, j = 0 dp\[0\]\[2\] = 1,表示在物品集合{2}中取出和为2的排列有{2}
|
|
||||||
i = 2, j = 1 dp\[1\]\[2\] = 1,表示在物品集合{2,3}中取出和为2的排列有{2}
|
|
||||||
i = 2, j = 2 dp\[2\]\[2\] = 1,表示在物品集合{2,3,4}中取出和为2的排列有{2}
|
|
||||||
|
|
||||||
1 0 1 0 0 0 0
|
|
||||||
1 0 1 0 0 0 0
|
|
||||||
1 0 **1** 0 0 0 0
|
|
||||||
|
|
||||||
此时dp\[2\]\[2\]作为第2列最底部的元素,表示所有物品都有的情况下和为2的排列方式有1个 (类比成一维dp即dp[2]=dp[0])
|
|
||||||
|
|
||||||
————————————————————————————
|
|
||||||
|
|
||||||
i = 3, j = 0 dp\[0\]\[3\] = 0,表示在物品集合{2}中无法取出和为3
|
|
||||||
i = 3, j = 1 dp\[1\]\[3\] = 1,表示在物品集合{2,3}中取出和为3的排列有{3}
|
|
||||||
i = 3, j = 2 dp\[2\]\[3\] = 1,表示在物品集合{2,3,4}中取出和为3的排列有{3}
|
|
||||||
|
|
||||||
1 0 1 0 0 0 0
|
|
||||||
1 0 1 1 0 0 0
|
|
||||||
1 0 1 **1** 0 0 0
|
|
||||||
|
|
||||||
此时dp\[2\]\[3\]作为第3列最底部的元素,表示所有物品都有的情况下和为3的排列方式有1个(类比成一维dp即dp[3]=dp[0])
|
|
||||||
|
|
||||||
————————————————————————————
|
|
||||||
|
|
||||||
i = 4, j = 0 dp\[0\]\[4\] = 1,表示在物品集合{2}中取出和为4的排列有在原有的排列{2}后添加一个2成为{2,2}(从第2列底部信息继承获得)
|
|
||||||
i = 4, j = 1 dp\[1\]\[4\] = 1,表示在物品集合{2,3}中取出和为4的排列有{2,2}
|
|
||||||
i = 4, j = 2 dp\[2\]\[4\] = 2,表示在物品集合{2,3,4}中取出和为4的排列有{{2,2},{4}}({2,2}的信息从该列头上获得)
|
|
||||||
|
|
||||||
1 0 1 0 1 0 0
|
|
||||||
1 0 1 1 1 0 0
|
|
||||||
1 0 1 1 **2** 0 0
|
|
||||||
|
|
||||||
此时dp\[2\]\[4\]作为第4列最底部的元素,表示所有物品都有的情况下和为4的排列方式有2个
|
|
||||||
|
|
||||||
————————————————————————————
|
|
||||||
|
|
||||||
i = 5, j = 0 dp\[0\]\[5\] = 1,表示在物品集合{2}中取出和为5的排列有{3,2} **(3的信息由dp[2]\[3]获得,即将2放在3的右边)**
|
|
||||||
i = 5, j = 1 dp\[1\]\[5\] = 2,表示在物品集合{2,3}中取出和为5的排列有{{2,3},{3,2}} **({3,2}由上一行信息继承,{2,3}是从dp[2] [2]获得,将3放在2的右边)**
|
|
||||||
i = 5, j = 2 dp\[2\]\[5\] = 2,表示在物品集合{2,3,4}中取出和为5的排列有{{2,3},{3,2}}
|
|
||||||
|
|
||||||
1 0 1 0 1 1 0
|
|
||||||
1 0 1 1 1 2 0
|
|
||||||
1 0 1 1 2 **2** 0
|
|
||||||
|
|
||||||
此时dp\[2\]\[5\]作为第5列最底部的元素,表示所有物品都有的情况下和为5的排列方式有2个
|
|
||||||
|
|
||||||
————————————————————————————
|
|
||||||
|
|
||||||
i = 6, j = 0 dp\[0\]\[6\] = 2,表示在物品集合{2}中取出和为6的排列有{{2,2,2},{4,2}} **(信息由dp[2]\[4]获得,即将2放在{2,2}和{4}的右边)**
|
|
||||||
i = 6, j = 1 dp\[1\]\[6\] = 3,表示在物品集合{2,3}中取出和为6的排列有{{2,2,2},{4,2},{3,3}} **({2,2,2},{4,2}由上一行信息继承,{3,3}是从dp[2] [3]获得,将3放在3的右边)**
|
|
||||||
i = 6, j = 2 dp\[2\]\[6\] = 4,表示在物品集合{2,3,4}中取出和为6的排列有{{2,2,2},{4,2},{3,3},{2,4}} **({2,2,2},{4,2},{3,3}由上一行继承,{2,4}从dp[2]获得,将4放在2的右边)**
|
|
||||||
|
|
||||||
1 0 1 0 1 1 2
|
|
||||||
1 0 1 1 1 2 3
|
|
||||||
1 0 1 1 2 2 **4**
|
|
||||||
|
|
||||||
此时dp\[2\]\[6\]作为第6列最底部的元素,表示所有物品都有的情况下和为6的排列方式有4个,为{2,2,2},{4,2},{3,3},{2,4}。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user