动态规划--背包问题总结

背包问题(物品下标从1开始)

0~1背包模版:

题目一般是要求:

给定一个容量给定的背包(就是物品体积和小于容量),然后有N个物品,每个的价值和每个的体积,然后每个物品能用1次或0次最大价值

方法步骤:

(1)状态表示:一般用二维数组      f[i][j]表示从前i个物品选体积小于等于j的最大价值。

(2)状态转移方程:

f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);//两部分取最大值

意思是f[i][j]包括两部分:

1.f[i-1][j]背包里不包括第i件物品,就是到前i-1体积就达到j,此时价值为f[i-1][j]。

2.f[i-1][j-v[i]]+w[i]   背包里包括第i个物品,也就是到第i-1件物品体积达到j-v[i],此时价值为f[i-1][j-v[i]]+w[i]

还有注意事项:

第一部分一定可以有

第二部分即包含第i个物品需要j>v[i]才能放第i个物品

方法一:用二维dp做0~1背包

 

 

 

查看代码

 #include<bits/stdc++.h>
using namespace std;
//f数组(动态规划数组dp)
int f[1200][1200];
//体积数组
int v[1200];
//价值数组
int w[1200];
int main(){
    int N,V;
    cin>>N>>V;
    for(int i=1;i<=N;i++){
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=N;i++){
        for(int j=0;j<=V;j++){
            f[i][j]=f[i-1][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][V];
    return 0;
    
}

方法二:用一维dp进行优化

 

 

 

查看代码

 #include<bits/stdc++.h>
using namespace std;
//f数组(动态规划数组dp)
int f[1200];
//体积数组
int v[1200];
//价值数组
int w[1200];
int main(){
int N,V;
cin>>N>>V;
for(int i=1;i<=N;i++){
cin>>v[i]>>w[i];
}
for(int i=1;i<=N;i++){
for(int j=V;j>=v[i];j--){
//能放得下v[i]
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[V];
return 0;

}

二维dp优化成一维dp的方法:

消除第一维,然后改写内层for

for(int j=V;j>=v[i];j--)从大到小

完全背包:

与0~1背包相比区别是:

每件物品可以选0~多件

 

查看代码

 #include<bits/stdc++.h>
using namespace std;
int v[1200];
int w[1200];
 int dp[1200][1200];
int main(){
    int N,V;
    cin>>N>>V;//N件物品,背包容量为V
    for(int i=1;i<=N;i++){
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=N;i++){
        for(int j=0;j<=V;j++){
            for(int k=0;k*v[i]<=j;k++){
               
                dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
            }
        }
    }
    cout<<dp[N][V];
    return 0;
}

 核心代码

  for(int i=1;i<=N;i++){
        for(int j=0;j<=V;j++){
            for(int k=0;k*v[i]<=j;k++){
               
                dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
            }
        }
    }

 

最外层遍历物品下标(从1开始),中层循环遍历体积,内层遍历第i种物品数

状态转移方程:

dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+w[i]*k);

//

包括第i种物品:

第i种物品可以有0~多件(0~k)

 优化上面方案

 

查看代码

#include<bits/stdc++.h>
using namespace std;
int v[1200];
int w[1200];
 int dp[1200][1200];
int main(){
    int N,V;
    cin>>N>>V;//N件物品,背包容量为V
    for(int i=1;i<=N;i++){
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=N;i++){
        for(int j=0;j<=V;j++){
          
               dp[i][j]=dp[i-1][j];
              if(j>=v[i])  dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
            
        }
    }
    cout<<dp[N][V];
    return 0;
}

优化方案:

原来:

dp[i][j]=max(dp[i-1][j-k*v[i]]+k*w[i]);

展开:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i],dp[i-1][j-2*v[i]]+2*w[i],dp[i-1][j-3*v[i]]+3*w[i]...);

展开:dp[i][j-v[i]]=max(dp[i-1][j-v[i]],dp[i-1][j-2*v[i]]+w[i],dp[i-1][j-3*v[i]]+2*w[i]...)

所以状态转移优化方程:dp[i][j]=max(dp[i-1][j],dp[i][j-v[i]]+w);

对上述代码进行1维优化

 

 

  

 

 

 

 

  0~1背包和完全背包总结

状态转移方程相同:

dp[j]=max(dp[j],dp[j-v[i]]+w[i])

外层循环i是1~n种物品

区别:

题意不同

0~1背包:每个物品有0~1件

完全背包:每个物品有0~多件

内层循环:

0~1背包:j从V到 v[i]

完全背包:j从v[i]到V

多重背包:

题意要求一般是:

给定一个容量给定的背包(就是物品体积和小于容量),然后有N个物品,每个的价值和每个的体积和每个物品限制个数。

 

 

 

  

 

 

对多重背包的一维优化

 

  

查看代码

#include<bits/stdc++.h>
using namespace std;
int v[11010];
int w[11010];
int dp[11010];
int main(){
    int N,V;
    cin>>N>>V;
    int a,b,s;
    int cnt=0;
    for(int i=1;i<=N;i++){
        cin>>a>>b>>s;//v,w,s
        int k=1;
        while(k<=s){
            cnt++;
            v[cnt]=a*k;
            w[cnt]=b*k; 
            s-=k;
            k*=2;
        }
        if(s){
            cnt++;
            v[cnt]=a*s;
            w[cnt]=b*s;
        }
    }
    int n=cnt;
    for(int i=1;i<=n;i++){
        for(int j=V;j>=v[i];j--){
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
        }
    }
    cout<<dp[V];
    return 0;
}

总结:

方法用的是合并

a,b,s 为当前品种的体积价值和数量限制

k:1,2,4,8

然后s不断减k,k为当前件数

最后剩余的s再乘上当前体积价值

还要cnt++

最后int n=cnt      

数组一般开多大:

向上取整(log2背包容量大小)+10;

分组背包问题:

vij表示第i种物品中的第j个物品的体积

wij表示第i种物品中的第j个物品的价值

 

 

 

 

查看代码

 #include<bits/stdc++.h>
using namespace std;
int v[110][110];
int w[110][110];
int s[110];
int dp[110];
int main(){
    int N,V;
    cin>>N>>V;
    for(int i=1;i<=N;i++){
        cin>>s[i];
        //每个品种的个数
        for(int j=0;j<s[i];j++){
            cin>>v[i][j]>>w[i][j];
        }
    }
    
    for(int i=1;i<=N;i++ ){
        for(int j=V;j>=0;j--){
            for(int k=0;k<s[i];k++){
                if(j-v[i][k]>=0)
          dp[j]=max(dp[j],dp[j-v[i][k]]+w[i][k]) ;     
            }
        }
    }
    cout<<dp[V];
    return 0;
}

代码分析:

1.物种下标从1开始,每个物种里的物品下标从0开始,即i从0开始,j从1开始

for(int i=1;i<=N;i++){
        cin>>s[i];
        //每个品种的个数
        for(int j=0;j<s[i];j++){
            cin>>v[i][j]>>w[i][j];
        }
    }

2.状态转移方程

dp[j ]=max(dp[j],dp[j-v[i][k]]+w[i][k]);

然后三层循环

外:遍历1~N个物种

中:遍历体积[V,0]

内:0=<k<s[i]

条件:j-v[i][k]>=0

因为下标从0开始

 

背包 总结(一维数组)

 

  • 0~1背包:(每件物品装入0~1件)

         体积j从V到v[i]

     dp[j]=max(dp[j],dp[j-v[i]]+w[i]);

  • 完全背包:(每件物品0~多件)

                体积j从v[i]到V

                dp[j]=max(dp[j],dp[j-v[i]]+w[i]);

  •  多重背包(每件物品0~限制的件数)

               体积j从V到v[i]

              dp[j]=max(dp[j],dp[j-v[i]]+w[i]);

             

  • 分组背包(多个组,每组里面又有很多物品,每个物品的体积,价值输入。

              体积 j从V到0

              条件:j-v[i][k]>=0

             dp[j]=max(dp[j],dp[j-v[i][k]]+w[i][k]);

             

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @   Annaprincess  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示