HDU1114 完全背包问题

玛德,数组开小了会超时。。。。

这篇文章就记录一下自己对于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;
}

猜你喜欢

转载自blog.csdn.net/qq_41755258/article/details/83098623