梦幻岛宝珠
题解:
这个题的原型是0/1背包
数据范围$n\leqslant 100 $, \(W\leqslant 2^{30}\)
普通0/1背包的时间复杂度\(O(nW)\),显然直接T飞
所以绝对不是一般的0/1背包
考虑一些奇怪的事情
注意到
\(w_i=a*2^b,a\leqslant10,b\leqslant 30\)
这个\(a\leqslant 10\)就是特殊性质了
发现如果挂个\(log\)啥都挺好说
考虑从指数\(b\)的角度入手
我们考虑对\(b\)一样的东西进行DP预处理
首先对相同的\(b\)进行一次背包DP
找出合并以后的价值和价格
然后合并就好了
关键是如何合并
(于是想到这里就中断了)
(思路继续)
对于每一位\(b\)
令\(f[b][j]\)为最高位为\(b\),最高位用掉的容积为\(j\)
这里默认剩余用掉的容积为\(Wand2^b-1\)
也就是\(W\)最低\(b\)位的值
然后就是转移了:
\(f[i][j]=max(f[i][j],g[i-1][k]+f[i-1][min(10*n,(j-k)*2]\)
目标:
\(f[logW][1]\)
这里\(logW\)就是\(W\)的位数
感性理解就当泛化背包了
Code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int o=5050;
int n,W,maxn;
vector<int>w[o],v[o];
ll f[40][o],g[40][o],len;
void load(){
for(int i=0;i<=100;i++){
w[i].clear();
v[i].clear();
}
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
}
void in(){
for(int i=1,wi,vi;i<=n;i++){
scanf("%d%d",&wi,&vi);
int cnt=0;
while(!(wi&1)){
wi>>=1;
cnt++;
}
maxn=max(wi,cnt);
w[cnt].push_back(wi);
v[cnt].push_back(vi);
}
len=log(W)/log(2);
}
void pre(){
for(int i=0;i<=len;i++){
if(!w[i].size()){
continue;
}
for(unsigned ll j=0;j<w[i].size();j++){//预处理0/1背包
for(int k=1000;k>=w[i][j];k--){
g[i][k]=max(g[i][k],g[i][k-w[i][j]]+v[i][j]);
}
}
}
}
void work(){
for(int i=0;i<=len;i++){
for(int j=1000;j>=0;j--){
for(int k=0;k<=j;k++){
f[i][j]=max(f[i][j],g[i][k]+f[i-1][(j-k)*2+(W>>(i-1)&1)]);
}
}
}
}
void out(){
cout<<f[len][1]<<endl;
}
int main(){
while(scanf("%d%d",&n,&W)!=EOF){
if(n==-1&&W==-1){
break;
}
load();
in();
pre();
work();
out();
}
return 0;
}

浙公网安备 33010602011771号