DAY44
闫氏DP
2 01背包问题
用滚动数组来优化空间,从后向前(大到小)遍历j
- #include<iostream>
- using namespace std;
- const int N=1010;
- int n,m;
- int v[N],w[N];
- int f[N][N];//所有只考虑前i个物品,**且总体积不超过j**的选法的集合。
- int main(){
- cin>>n>>m;
- //背包当前体积j 是第二维度
- for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
- for(int i=1;i<=n;i++){
- //从0开始检查:前i个物体,使得背包体积为0,合法吗:合法,因为可以都不选
- for(int j=0;j<=m;j++){
- f[i][j]=f[i-1][j]; //left;
- //右半边不一定存在,当前体积小于v[i],但是i又在背包。矛盾,不合法
- //这里因为只计算变的情况下对应的当前背包体积,所以是[j-v[i]]
- if(j>=v[i])f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
- }
- }
- cout<<f[n][m]<<endl;
- return 0;
- }
空间优化:
- #include<iostream>
- using namespace std;
- const int N=1010;
- int n,m;
- int v[N],w[N];
- int f[N];//所有只考虑前i个物品,**且总体积不超过j**的选法的集合。
- int main(){
- cin>>n>>m;
- //背包当前体积j 是第二维度
- for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
- for(int i=1;i<=n;i++)
- //从0开始检查:前i个物体,使得背包体积为0,合法吗:合法,因为可以都不选
- for(int j=m;j>=v[i];j--)
- f[j]=max(f[j],f[j-v[i]]+w[i]);
- cout<<f[m]<<endl;
- return 0;
- }
优化思想:代码等价即可。
完全背包问题
记:把01背包问题改成for(int j=v[i];j<=m;j++)即可
笔记见纸质版。
J=0 或j=1起步都可以
- #include<iostream>
- using namespace std;
- const int N=1010;
- int n,m;
- int v[N],w[N];
- int f[N][N];
- int main(){
- cin>>n>>m;
- for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
- for(int i=1;i<=n;i++){
- for(int j=1;j<=m;j++){
- f[i][j]=f[i-1][j];
- if(j>=v[i]) f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
- }
- }
- cout<<f[n][m]<<endl;
- return 0;
- }
01背包问题 二维
- 暴力法:回溯
回溯算法--01背包问题_回溯法01背包问题-CSDN博客
学过了吗:学好了,但是和代码随想录的回溯模板不一样,不太适应。比较陌生,看来需要二刷回溯。
- 二维数组法:
- #include<iostream>
- using namespace std;
- const int N=5010;
- int v[N],w[N];
- int dp[N][N];
- int n,m;
- int main(){
- cin>>n>>m;
- for(int i=1;i<=n;i++) cin>>v[i];
- for(int i=1;i<=n;i++) cin>>w[i];
- for(int i=1;i<=n;i++){
- for(int j=0;j<=m;j++){
- dp[i][j]=dp[i-1][j];
- if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
- }
- }
- cout<<dp[n][m];
- return 0;
- }
- 滚动数组优化
- #include<iostream>
- using namespace std;
- const int N=5010;
- int v[N],w[N];
- int dp[N];
- int n,m;
- int main(){
- cin>>n>>m;
- for(int i=1;i<=n;i++) cin>>v[i];
- for(int i=1;i<=n;i++) cin>>w[i];
- for(int i=1;i<=n;i++){
- for(int j=m;j>=v[i];j--){
- dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
- }
- }
- cout<<dp[m];
- return 0;
- }
01背包问题 一维
也就是3.滚动数组优化
416分割等和子集
- 贪心法,做不了,看例子:
- 动态规划:据说是01背包的应用,但是我想不出来怎么应用
这样想(也是我没想出来的点):两个子集的各自sum相等,那么它们的SUM应当等于原集合sum/2
// 每一个元素一定是不可重复放入,所以从大到小遍历
01背包问题:动态规划的思路一个一个物品去尝试,一点点扩大考虑能够容纳的容积大小,整个过程像是在填一张二维表格。
这题:设置状态:dp[i][j]表示考虑下标[0,i]这个区间里的所有整数,在它们当中是否能够选出一些数,使得这些数之和恰好为整数j。
优质题解,还分享了很多资料。
注意闫氏DP法的运用:在声明数组含义时候,需要明白每个下标的含义,然后再去denote DP数组的含义——满足**条件(每个下标)的**,再表示它的值。
学了很久,终于开始写代码了,见证自己的毅力哈哈:
- class Solution {
- public:
- //代码随想录解法:能用一维就用一维,语句复杂反而不容易通过。
- bool canPartition(vector<int>& nums) {
- int sum=0;
- for(auto n:nums) sum+=n;
- if(sum%2==1) return false;
- sum/=2;
- vector<int> dp(20010,0);
- for(int i=0;i<nums.size();i++){
- for(int j=sum;j>=nums[i];j--){
- dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
- }
- }
- return dp[sum]==sum;
- }
- };
- class Solution {
- public:
- //二维力扣题解:
- bool canPartition(vector<int>& nums) {
- int sum=0;
- for(int n:nums) sum+=n;
- if(sum%2==1) return false;
- sum/=2;
- int len=nums.size();
- vector<vector<bool>> dp(len,vector<bool>(sum+1,false));
- for(int i=0;i<len;i++) {
- dp[i][0]=true;
- }
- if(sum>=nums[0]) dp[0][nums[0]]=true;
- //这里下标有细节,配合上一句一起用,那么i从1开始
- for(int i=1;i<len;i++){
- for(int j=0;j<=sum;j++)
- {
- //别写错
- dp[i][j]=dp[i-1][j];
- if(j>=nums[i]) dp[i][j]=dp[i][j]||dp[i-1][j-nums[i]];
- }
- }
- return dp[nums.size()-1][sum];
- }
- };
- class Solution {
- public:
- //力扣官方+一维滚动数组
- bool canPartition(vector<int>& nums) {
- int sum=0,max=INT_MIN;
- for(auto n:nums) {
- sum+=n;
- if(n>max) max=n;
- }
- if(sum%2==1) return false;
- sum/=2;
- if(max>sum) return false;
- vector<bool> dp(sum+1,false);
- dp[0]=true;
- //照抄上一行,所以是i=1开始
- for(int i=1;i<nums.size();i++){
- //从后向前遍历
- for(int j=sum;j>=nums[i];j--)
- dp[j]=dp[j]||dp[j-nums[i]];
- }
- return dp[sum];
- }
- };
- class Solution {
- public:
- // 精选题解:
- bool canPartition(vector<int>& nums) {
- int sum = 0;
- for (auto n : nums)
- sum += n;
- if (sum % 2 == 1)
- return false;
- sum /= 2;
- vector<vector<bool>> dp(nums.size(), vector<bool>(sum + 1, false));
- if (sum >= nums[0])
- dp[0][nums[0]] = true;
- for (int i = 1; i < nums.size(); i++) {
- for (int j = 0; j <= sum; j++) {
- dp[i][j] = dp[i-1][j];
- if (nums[i] == j) {
- dp[i][j] = true;
- continue;
- }
- if (j > nums[i])
- dp[i][j] = dp[i][j] || dp[i-1][j - nums[i]];
- }
- }
- return dp[nums.size() - 1][sum];
- }
- };
- class Solution {
- public:
- // 精选题解:
- bool canPartition(vector<int>& nums) {
- int sum = 0;
- for (auto n : nums)
- sum += n;
- if (sum % 2 == 1)
- return false;
- sum /= 2;
- vector<vector<bool>> dp(nums.size(), vector<bool>(sum + 1, false));
- if (sum >= nums[0])
- dp[0][nums[0]] = true;
- for (int i = 1; i < nums.size(); i++) {
- for (int j = 0; j <= sum; j++) {
- dp[i][j] = dp[i-1][j];
- if (nums[i] == j) {
- dp[i][j] = true;
- continue;
- }
- if (j > nums[i])
- dp[i][j] = dp[i][j] || dp[i-1][j - nums[i]];
- if(dp[i][sum]) return true;
- }
- }
- return dp[nums.size() - 1][sum];
- }
- };
- class Solution {
- public:
- // 精选题解:
- bool canPartition(vector<int>& nums) {
- int sum = 0;
- for (auto n : nums)
- sum += n;
- if (sum % 2 == 1)
- return false;
- sum /= 2;
- vector<bool> dp(sum+1, false);
- dp[0] = true;
- //下一句不能少:他作为第一行
- if(sum>=nums[0]) dp[nums[0]]=true;
- for (int i = 1; i < nums.size(); i++) {
- for (int j = sum; j >= nums[i]; j--) {
- dp[j] = dp[j] || dp[j - nums[i]];
- }
- }
- return dp[sum];
- }
- };