代码随想录 | Day38 | 动态规划 :01背包应用 目标和&&一和零
难点:
代码都不难写,如何想到01背包并把具体问题抽象为01背包才是关键
这真的不能怪笔者拖更,这两道题真给我整麻了
文章目录
- [代码随想录 | Day38 | 动态规划 :01背包应用 目标和&&一和零](#代码随想录 | Day38 | 动态规划 :01背包应用 目标和&&一和零)
494.目标和(恰好等于背包容量求方案数)
[494. 目标和 - 力扣(LeetCode)](https://leetcode.cn/problems/target-sum/description/)\](https://leetcode.cn/problems/partition-equal-subset-sum/description/)
#### 思路分析:
设前面要加"+"的数和为p,前面要加"-"的数的和为q。
p+q=sum(数组所有元素的和)
p-q=target(要加正号的减去要加负号的)
2p=sum+target
p=(sum+target)/2
也就是说呢,我们要在nums数组里面找一个子集,让子集的和等于p,能找到几个就有几种方案
#### 1.回溯法
**本题也可以使用回溯暴力枚举,直接搜索nums里面的所有组合,等于target的就是答案。**
这是组合总和的代码,当然是超时的
```cpp
class Solution {
private:
vector 关于初始化就看上面回溯的终止条件 i<0&&c==0就为1,那里的i是我们这里的i-1,因为我们给dp数组的i下标都加了1 也就是说 dp[i][0]全都为1 但这里只需要把dp[0][0]=1就行,其他的会在循环中给他赋值的,最后打印出来也确实是1 完整代码: 这道题我也没想到怎么联系到01背包,或者说怎么应用 看了题解以后才了解到了 咱们平时的容量限制是物品i的重量 今天的容量限制是物品i(字符串strs[i])里面的0和1的数量,就是说有两个限制,一个是0的数量,一个是1的数量,说明这是一个三维数组,而每个物品i(字符串strs[i])的价值是多少? 因为求的是子集的数量,所以每一个字符串的价值都为1,就是放这个字符串i进去,就加1,不放就不加 举例: 比如说我们放的放的,背包容量只能装2个0和三个1了 那我们下一个strs[i]如果是 000111,有3个0,3个1那也放不进去 001111也不行 0000也不行 就是两个要都满足 比如0011,然后放进去以后最大的方案数+1 所以本质上就是01背包的容量多加了一个维度 递推公式直接仿照着写 当前的最大数量那就是不放当前字符串和放了当前字符串这两种情况里面选一个最大值,如果我我们选了当前字符串那就+1,因为dp含义是在前i个字符串里面选的最多有j个0和k个1的子集中的字符串数量。 1.参数和返回值 i是物品编号,标识到了第几个字符串了 j是当前0的容量,k是当前1的容量 strs是题数组 dfs(i,j,k,strs) 含义是在前i个字符串里面选的最多有j个0和k个1的子集中的字符串数量 2.终止条件 1.i<0说明没有物品可以选了,如果当前容量正好等于0,那就返回1说明找到了一个合法方案 不等于0就返回0说明没找到 2.如果当前容量已经小于了物品所需的容量,那说明背包装不下,那就只能不选这个物品了,就返回不选这个物品的方案数量,即在前i-1个物品里面能凑够容量c的方案数 3.本层逻辑 返回当前的最大数量,就是不放当前字符串和放了当前字符串这两种情况里面选一个最大值 完整代码 当然是超时的 还是老样子 初始化dp为-1,如果不是-1说明计算过了直接返回 并且在返回之前给dp赋值 多加了初始化部分,就是物品1,能放下的地方初始化为1,放不下的初始化为0 完整代码: 和01背包一样,先遍历物品 容量倒着遍历 删掉物品编号那一维 除了初始化为0不需要其他的初始化动作
dp[i+1][j]=dp[i][j]+dp[i+1][j-nums[i]];
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum=0;
for(int c:nums)
sum+=c;
int p=sum+target;
if (p < 0 || p % 2 != 0)
return 0;
target=p/2;
vector<vector<int>> dp(nums.size()+1,vector<int>(target+1,0));
dp[0][0]=1;
for(int i=0;i<nums.size();i++)
for(int j=0;j<=target;j++)
if(j<nums[i])
dp[i+1][j]=dp[i][j];
else
dp[i+1][j]=dp[i][j]+dp[i][j-nums[i]];
return dp[nums.size()][target];
}
};
4.滚动数组优化
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum=0;
for(int c:nums)
sum+=c;
int p=sum+target;
if (p < 0 || p % 2 != 0)
return 0;
target=p/2;
vector<int> dp(target+1,0);
dp[0]=1;
for(int i=0;i<nums.size();i++)
for(int j=target;j>=nums[i];j--)
dp[j]=dp[j]+dp[j-nums[i]];
return dp[target];
}
};
474.一和零
思路分析:
dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i]);
dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j - zeronum][k - onenum] + 1);
1.回溯暴力枚举
int dfs(int i,int j,int k,vector<string>& strs)
if(i<0)
return 0;
if(j<zeronum || k<onenum)//0和1只要有一个不够就不放
return dfs(i-1,j,k,strs);
int zeronum=0;
int onenum=0;
for(auto c:strs[i])
if(c=='0')
zeronum++;
else
onenum++;
if(j<zeronum || k<onenum)
return dfs(i-1,j,k,strs);
return max(dfs(i-1,j,k,strs),dfs(i-1,j-zeronum,k-onenum,strs)+1);
class Solution {
public:
int dfs(int i,int c,vector<int>& nums)
{
if(i<0)
if(c==0)
return 1;
else
return 0;
if(c<nums[i])
return dfs(i-1,c,nums);
return dfs(i-1,c-nums[i],nums)+dfs(i-1,c,nums);
}
int findTargetSumWays(vector<int>& nums, int target) {
int sum=0;
for(int c:nums)
sum+=c;
int p=sum+target;
if (p < 0 || p % 2 != 0)
return 0;
target=p/2;
return dfs(nums.size()-1,target,nums);
}
};
2.记忆化搜索
class Solution {
public:
int dfs(int i,int j,int k,vector<string>& strs,vector<vector<vector<int>>>& dp)
{
if(i<0)
return 0;
int zeronum=0;
int onenum=0;
for(auto c:strs[i])
if(c=='0')
zeronum++;
else
onenum++;
if(dp[i][j][k]!=-1)
return dp[i][j][k];
if(j<zeronum || k<onenum)
return dp[i][j][k]=dfs(i-1,j,k,strs,dp);
return dp[i][j][k]=max(dfs(i-1,j,k,strs,dp),dfs(i-1,j-zeronum,k-onenum,strs,dp)+1);
}
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<vector<int>>> dp(strs.size(),vector<vector<int>>(m+1,vector<int>(n+1,-1)));
return dfs(strs.size()-1,m,n,strs,dp);
}
};
//lambda
虚晃一枪,笔者懒得写了,大家自己写吧
3. 1:1翻译为动态规划
int zeronum=0;
int onenum=0;
for(auto c:strs[0])
if(c=='0')
zeronum++;
else
onenum++;
for(int j=0;j<=m;j++)
for(int k=0;k<=n;k++)
if(j<zeronum||k<onenum)
dp[0][j][k]=0;
else
dp[0][j][k]=1;
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<vector<int>>> dp(strs.size(),vector<vector<int>>(m+1,vector<int>(n+1,0)));
int zeronum=0;
int onenum=0;
for(auto c:strs[0])
if(c=='0')
zeronum++;
else
onenum++;
for(int j=0;j<=m;j++)
for(int k=0;k<=n;k++)
if(j<zeronum||k<onenum)
dp[0][j][k]=0;
else
dp[0][j][k]=1;
for(int i=1;i<strs.size();i++)
{
zeronum=0;
onenum=0;
for(auto c:strs[i])
if(c=='0')
zeronum++;
else
onenum++;
for(int j=0;j<=m;j++)
for(int k=0;k<=n;k++)
if(j<zeronum||k<onenum)
dp[i][j][k]=dp[i-1][j][k];
else
dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-zeronum][k-onenum]+1);
}
return dp[strs.size()-1][m][n];
}
};
4.滚动数组优化
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m+1,vector<int>(n+1,0));
for(int i=0;i<strs.size();i++)
{
int zeronum=0;
int onenum=0;
for(auto c:strs[i])
if(c=='0')
zeronum++;
else
onenum++;
for(int j=m;j>=zeronum;j--)
for(int k=n;k>=onenum;k--)
dp[j][k]=max(dp[j][k],dp[j-zeronum][k-onenum]+1);
}
return dp[m][n];
}
};