BZOJ 1190 梦幻岛宝珠(分组01背包)
跑了7000ms。。。
这是个体积和价值都超大的背包。但是体积保证为a*2^b的(a<=10,b<=30)形式.且n<=100.
于是可以想到按b来分组。这样的话每组最多为a*n*2^b的体积。把每个分组进行一次01背包。
令dp[i][j]表示体积为j*2^i+w&(1<<i)的最大价值。再把每个分组背包做一次01背包,随便转移一下就行了。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-9 # define MOD 1000000000 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r //# define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=105; //Code begin... int dp[35][1005]; int main () { int n, W, x, val, wei, tmp; while (scanf("%d%d",&n,&W)) { if (n==-1) break; tmp=W; wei=0; mem(dp,0); while (tmp) ++wei, tmp>>=1; FOR(i,1,n) { scanf("%d%d",&x,&val); int num=0; while (x) { ++num; if (x&1) break; x>>=1; } for (int i=1000; i>=x; --i) dp[num][i]=max(dp[num][i],dp[num][i-x]+val); } FOR(i,1,wei) for (int j=1000; j>=0; --j) for (int k=j; k>=0; --k) { int l=min(((j-k)<<1)+((W>>(i-2))&1),1000); dp[i][j]=max(dp[i][j],dp[i][k]+dp[i-1][l]); } printf("%d\n",dp[wei][1]); } return 0; }