BZOJ 1190: [HNOI2007]梦幻岛宝珠 背包DP

题意

大背包问题
物品重量可写成a*2^b的形式(1<=a<=10,0<=b<=30)

(实测int能过,就没写longlong)

题解1

直接背包肯定TLE+MLE

考虑到每个weight都能写成a*2^b的形式,显然我们要按照b分层来进行背包

令f[i][j]表示有j*2^i+(w&(1<<i)-1)的空间时的最大价值

首先每层内部先做一个01背包,因为a<=10,n<=100,所以容量最大为1000

然后层与层之间再转移

从大到小枚举j 转移方程为f[i][j]=max{f[i][j],f[i][j-k]+f[i-1][min(k*2+((w>>i-1)&1),1000)]}

转自PoPoQQQ的博客

CODE1

时间复杂度O(1001000+3010001000)O(100*1000+30*1000*1000)

慢。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 105;
const int MAXM = 1000;
const int LOG = 30;
int n, W, f[LOG+5][MAXM+5];

int main () {
	while(scanf("%d%d", &n, &W)) {
		if(n == -1 || W == -1) return 0;
		memset(f, 0, sizeof f);
		for(int i = 1, a, b, val; i <= n; ++i) {
			b = 0;
			scanf("%d%d", &a, &val);
			while(b <= 30 && !(a&1)) ++b, a>>=1;
			for(int j = MAXM; j >= a; --j)
				f[b][j] = max(f[b][j], f[b][j-a] + val);
		}
		int ans = 0;
		for(int i = 0; i <= min(MAXM, W); ++i)
			ans = max(ans, f[0][i]);
		for(int i = 1; i <= LOG && (1<<i) <= W; ++i)
			for(int j = min(MAXM, W>>i); j >= 0; --j) {
				for(int k = 0; k <= j; ++k)
					f[i][j] = max(f[i][j], f[i][j-k] + f[i-1][min(MAXM, (k<<1)+((W>>i-1)&1))]);
				ans = max(ans, f[i][j]);
			}
		printf("%d\n", ans);
	}
}

题解2

按b从大到小DP,继承上一层的答案后直接01背包。因为是从高位到低位,不会对后面有影响。

此处状态定义为 f[i][j]f[i][j]表示可用容量为j2ij*2^i的最大价值。

然后每次减小a10a\le 10,一定不会减小超过10001000,每一层只开10001000就行了。

CODE2

时间复杂度O(1000100+301000)O(1000*100+30*1000)

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 105, MAXM = 1000, LOG = 30;
int n, W, f[LOG+5][MAXM+5];
vector<pair<int,int> >vec[MAXN];
int main () {
	while(scanf("%d%d", &n, &W)) {
		if(n == -1 || W == -1) return 0;
		for(int i = 0; i <= LOG; ++i) vec[i].clear();
		for(int i = 1, a, b, val; i <= n; ++i) {
			b = 0; scanf("%d%d", &a, &val);
			while(b <= 30 && !(a&1)) ++b, a>>=1;
			vec[b].push_back(pair<int,int>(a, val));
		}
		memset(f, -0x3f, sizeof f); f[LOG+1][0] = 0;
		for(int i = LOG; i >= 0; --i) {
			for(int j = 0; j <= MAXM; ++j)
				f[i][min(MAXM, (j<<1)|((W>>i)&1))] = max(f[i][min(MAXM, (j<<1)|((W>>i)&1))], f[i+1][j]);
			for(int siz = vec[i].size(), k = 0; k < siz; ++k)
				for(int j = 0; j <= MAXM - vec[i][k].first; ++j)
					f[i][j] = max(f[i][j], f[i][min(MAXM, j+vec[i][k].first)] + vec[i][k].second);
		}
		printf("%d\n", *max_element(f[0], f[0] + 1000 + 1));
	}
}
posted @ 2019-12-14 14:50  _Ark  阅读(88)  评论(0编辑  收藏  举报