题意
大背包问题
物品重量可写成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)]}
CODE1
时间复杂度
慢。
#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
", ans);
}
}
题解2
按b从大到小DP,继承上一层的答案后直接01背包。因为是从高位到低位,不会对后面有影响。
此处状态定义为 表示可用容量为的最大价值。
然后每次减小,一定不会减小超过,每一层只开就行了。
CODE2
时间复杂度
#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
", *max_element(f[0], f[0] + 1000 + 1));
}
}