九大类背包问题专题1---01背包问题(二维和优化一维法附代码)

1. 01背包问题

问题:
有N件物品和一个容量是V的背包。
第i件物品的体积是vi,价值是wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包的容量,且价值最大。

输入格式
第一行有两个整数,N,V用空格隔开,分别表示物品数量和背包容积。
接下来有N行,每行两个整数vi,wi,用空格隔开,分别表示第i件物品的体积和价值。

输出格式
输出一个整数,表示最大价值

数据范围
0<N.V<=1000
0<vi,wi<=1000

输入样例
4 5
1 2
2 4
3 4
4 5

输出样例
8

分析思路:

二维动态规划
f[i][j]:只看前i个物品,当前使用所有的体积是j的情况下,最价值最大是多少

结果:
f[n][0-v],从0到v枚举,取最大值,考虑n件物品,体积从0,1,2,3...V,挑选最大的,
result={max[n][0-v]}

f[i][j]  j假设当前第i-1个物品都已经算完了,现在考虑的物品是i,有两种选择:
1.不选第i个,问题就变为只考虑前i-1个物品,体积为j的情况下最大价值
f[i][j]=f[i-1][j]
2.选第i个物品,需要从j里面把第i个物品的体积先去掉,则当前剩余体积为j-v[i]
f[i-1][j-v[i]]

答案:
f[i][j]=max{1,2}

初始化
f[0][0]=0; 一个物品都不考虑的情况下,体积只能为0

时间复杂度:
O(n*n)=10^6

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=1010;  //物品个数
int n,m;  //n:物品个数,m:总容量
int f[N][N];
int v[N],w[N];  //每个物品,及其价值
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) //从1开始
	cin>>v[i]>>w[i];
	for(int i=1;i<=n;i++)
	 for(int j=m;j>=v[i];j--){ //所有体积,从前往后枚举
	 f[i][j]=f[i-1][j]; //选的话,从f[i-1]
	    if(j>=v[i]) //判断,j<第i个物品的体积,无法选
	   f[j]=max(f[j],f[[i-1][j-v[i]]+w[i]);
	   }
	   int res=0;
	   for(int i=0;i<=m;i++)
	   res=max(res,f[i]);
	   cout<<res<<endl;
	   return 0;
} 

优化:

1.二维数组变为一维数组
每次计算的时候只和前一层有关,不需要把所有的都记录
一维数组
f[i]表示体积是i的情况下,最大价值是多少
压缩一维
改变循环顺序,把第一种选择f[i-1]去掉,从小到大枚举
算f[j]时,用f[j-v[i]],算第二种选择的时候尽量保证f[j-v[i]]之前没有算过,所以算第一种的时候用的是f[i][j-v[i]]
算f[j]时,f[j-v[i]]就已经算过了。

怎样保证算f[j]时,f[j-v[i]]这个状态是f[i-1]的状态,而不是f[i]的.->从大到小枚举

在用f[j-v[i]]这个状态时,f[j-v[i]]一定小于j,所以它一定没有算过。它就一定是f[i]的状态。

2.把枚举从0-m循环去掉
初始化并不是只把f[0]初始化为0,初始化的时候把所有的f[i]都变为0.
f[m]表示所有体积小于等于m的情况下,最大价值,而并不是体积恰好等于m的情况下最大价值

假设:最优选法,总体积是k
k<m的话,f[k]就是最大价格,
k是从f[0]转出来的

若求体积恰好是m的情况下,最大价值
在初始化的时候
f[0]=0;
f[i]=负无穷;
确保所有状态从f[0]转过来

优化代码:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=1010;
int n,m; 
int f[N];
int v[N],w[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=m;j>=v[i];j--)
	   f[j]=max(f[j],f[j-v[i]]+w[i]);
	   //初始化的时候把所有的f[i]都变为0
	   //原来:f[0]=0;
	   //现在: f[i]=0  
	   cout<<f[m]<<endl;
	   return 0;
} 

在这里插入图片描述


以上内容若有不足之处,欢迎评论指教。
声明:本人博客在未经允许的情况下,严谨他人转载或抄袭。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值