梦幻岛宝珠「HNOI2007」

题意

\(n\)个宝石,每个宝石都有重量与价值,求总重量不超过\(W\)时的最大价值。\(W\leq 2^{32}\)

保证每个宝石的重量都可以被表示为\(a*2^b\)的形式。


思路

分组背包,对\(b\)分组,在每一组内跑01背包。

每一组之间的转移有\(f[i][j]=max(f[i][j],f[i][k]+f[i-1][min(1000,(j-k)<<1)+((W>>(i-1))&1)])\)

代码

#include <bits/stdc++.h>

using namespace std;

namespace StandardIO {

    template<typename T> inline void read (T &x) {
        x=0;T f=1;char c=getchar();
        for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
        for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    template<typename T> inline void write (T x) {
        if (x<0) putchar('-'),x=-x;
        if (x>=10) write(x/10);
        putchar(x%10+'0');
    }

}

using namespace StandardIO;

namespace Solve {
	
	const int N=1001;
	
	int n,W,ans;
	int w[N],v[N];
	int f[N][N];

    inline void MAIN () {
		while (1) {
			read(n),read(W);
			if (n==-1) return;
			memset(f,0,sizeof(f)),ans=0;
			for (register int i=1; i<=n; ++i) {
				read(w[i]),read(v[i]);
				int b=0,a=w[i];
				while (!(a&1)) a>>=1,++b;
				for (register int j=min(1000,W>>b); j>=a; --j) {
					f[b][j]=max(f[b][j],f[b][j-a]+v[i]);
					ans=max(ans,f[b][j]);
				}
			}
			for (register int i=1; (1<<i)<W; ++i) {
				for (register int j=min(1000,W>>i); j>=0; --j) {
					for (register int k=j; k>=0; --k) {
						f[i][j]=max(f[i][j],f[i][k]+f[i-1][min(1000,(j-k)<<1ll)+((W>>(i-1))&1ll)]);
						ans=max(ans,f[i][j]);
					}
				}
			}
			write(ans),putchar('\n');
		}
    }

}

int main () {
    Solve::MAIN();
}
posted @ 2019-08-20 19:05  Ilverene  阅读(152)  评论(0编辑  收藏  举报