背包的几个问题
1271:【例9.15】潜水员
二位费用的背包
背包容量 氧气氮气的体积
背包费用 罐子的重量
求费用最小值
倒着for两维体积,原因是如果正着for,可能一件物品多次放到背包里
1295:装箱问题
状压如下,分析复杂度:n<=30,O((1<<30 ) * 30)=O(2e9 *30)= 6e10
肯定会T的!
for(int i=0;i<=(1<<n);i++){//2e9
int ans0=0;int t=i;
int num=n;while(t && num){//30
if(t&1) ans0+=ti[num];
t/=2;
num--;
}
if(ans0 <= v && ans0>ans) ans=ans0;
}
ans=v-ans;
cout << ans <<endl;
二位数组如下
cin>>v>>n;
for(int i=1;i<=n;i++) cin>>ti[i];
for(int i=1;i<=n;i++){
for(int j=v;j>=ti[i];j--){
if(f[i-1][j-ti[i]]+ti[i] <= v)
f[i][j]=max(f[i][j],f[i-1][j-ti[i]]+ti[i]);
}
}
for(int i=1;i<=n;i++) ans=max(ans,f[i][v]);
ans=v-ans;
cout<<ans<<endl;
然鹅错了
滚动数组如下
cin>>v>>n;
for(int i=1;i<=n;i++) cin>>ti[i];
for(int i=1;i<=n;i++){
for(int j=v;j>=ti[i];j--){
//if(f[j-ti[i]]+ti[i] <= v)
f[j]=max(f[j],f[j-ti[i]]+ti[i]);
if(f[j] > ans && f[j] < v) ans=f[j];
}
}
//ans=f[v];
ans=v-ans;
cout<<ans<<endl;
是错的,错误答案和二位数组一样
另一个如下
cin>>v>>n;
for(int i=1;i<=n;i++) cin>>ti[i];
for(int i=1;i<=n;i++){
for(int j=v;j>=ti[i];j--){
if(f[j-ti[i]]+ti[i] <= v)
f[j]=max(f[j],f[j-ti[i]]+ti[i]);
//if(f[j] > ans && f[j] < v) ans=f[j];
}
}
ans=f[v];
ans=v-ans;
cout<<ans<<endl;
这便对了。是1维的,不记录无用状态的一维的。
对于1、2维,我是这么想的:如果是2维,意味着第i个物品只能使用前i个物品的状态,
如果是1维,第i 个物品还能使用同样是第i 个物品,但是体积更小的状态。但是这样可能会出现第i个物品选两次的情况不是吗?但是如果倒着枚举就不会了(hh我就是倒着枚举的)
哦,好像应该这么解释:如果是一维的,那么第i 个物品相当于使用的是前1~i-1的所有物品的状态,所以会更优。
但是根据这个,我们可以给二维的程序加一句:
f[i][j]=max(f[i][j],f[i-1][j]);
便可达到同一维数组一样的效果。
然鹅还不对
经过调试,我发现了 问题所在:
1.j应该枚举到0,而不是枚举到ti[i] ,这样才能正确的顺延
2.其实if(f[j-ti[i]]+ti[i] <= v) 这一句是没用的,因为j的范围和f [i] [j]的范围是一样的,既然j<v,f [i] [j] 也就不会大于v了
最终的二维代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
int v,n,ti[33];
int ans;
int f[33][20010];
int main()
{
cin>>v>>n;
for(int i=1;i<=n;i++) cin>>ti[i];
for(int i=1;i<=n;i++){
for(int j=v;j>=0;j--){
f[i][j]=max(f[i][j],f[i-1][j]);
if(j >= ti[i])
f[i][j]=max(f[i][j],f[i-1][j-ti[i]]+ti[i]);
}
}
ans=f[n][v];
ans=v-ans;
cout<<ans<<endl;
return 0;
}
/*
10 3
1
5
7
*/