玛德,数组开小了会超时。。。。
这篇文章就记录一下自己对于01背包到完全背包一维数组转换的理解。推荐去看《背包九讲》
as we all know,01背包的二维数组的转移方程就是:(这个真的是超级重要,后面的其他背包问题都可以看作是01背包问题延申出来的。嗯,我是个渣渣~我是看网上的大佬都这样说)
for(int i=0;i<n;i++){
for(int j=0;j<=M;j++){
if(w[i]<=j) dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])
else dp[i][j]=max(dp[i-1][j],dp[i][j]);
}
};
时间复杂度不能再优化了,只能优化空间复杂度:
我们的二维数组是怎样实现的呢?外面大循环for i 0—n(有n个背包),里面是for j 0—M(M是最大的容量)。然后,通过遍历每一个背包来更新dp。
那么一维数组dp[i] for i 0—M,dp[i]能不能根据记录dp[i-1]更新的数据再进行更新呢?dp[i-1]的数据怎样保存呢?
for(int i=0;i<n;i++){
for(int j=M;j>=0;j--){
if(w[i]<=j){
dp[j]=max(dp[i],dp[i-w[i]]+v[i]);
}
}
}
首先最外层肯定是一个大循环,但是里面的质量就要倒叙遍历for j M—0,为什莫要这样呢?
假设是正序遍历,for i 0—M,给下列数据:
m: 5
w:2 1 1
v:100 2 3
如果是顺序遍历的话,2—100的这个背包会被重复放进去,反序的话,刚好可以。
(ps:前面居然会出现重复的情况,呀,Do you konw 完全背包?完全背包就是就是在01背包的基础上,再加上一个条件:每一个背包都是可以无限次重复。那这个顺序的一位数组解法不就刚好适用于完全背包的解法嘛,居然阴差阳错的凑成了完全背包的解法,hha,其实记住转移方程的最好的解法还是记住推导的过程)
一维数组dp[j]中表示的就是二维数组中我们定义的状态dp[i][j],dp[j-w[i]]就是原来的dp[i][j-w[i]]。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=100000+10;
int v[maxn];
int w[maxn];
int dp[maxn];
#define inf 0x3f3f3f3f
int main()
{
int T;
cin >> T;
while(T--)
{
int n,m;
cin >> n >> m;
m=m-n;
int cnt,i,j;
cin >> cnt;
for(i=0;i<cnt;i++){
cin >> v[i] >> w[i];
}
memset(dp,inf,sizeof(dp));
dp[0]=0;
for(i=0;i<cnt;i++){
for(j=w[i];j<=m;j++){
dp[j]=min(dp[j],dp[j-w[i]]+v[i]);
}
}
if(inf==dp[m]){
printf("This is impossible.\n");
}else{
cout << "The minimum amount of money in the piggy-bank is " << dp[m] << "." << endl;
}
}
return 0;
}