传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=1190
【题解】
首先,我们把所有物品都分解成$a\times 2^b$的形式,然后把物品按照$b$分组;
我们按$b$从高到低考虑。$f(i,j)$表示考虑到$2^i$,当前还剩余$j\times 2^i$的空间,所能取到的最大值。
每层先从上一层传递$f$数组,然后再更新。每次就是一个背包转移了。
考虑这个$j$可能随着$b$减小越来越大,我们需要优化。
对于每一层的物品,最多只能表示成$na\times 2^b$的形式,即最多$1000 \times 2^b$。
所以对于上一层传下来的值,如果它乘了2之后大于1000,实际上这些多于1000的部分是没有用的,我们可以直接舍弃!
这样的话就能保证复杂度了!
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
# include <vector> # include <stdio.h> # include <string.h> # include <iostream> # include <algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; const int M = 5e5 + 10; const int mod = 1e9 + 7; int n, W; vector<int> w[32], v[32]; ll f[32][1010]; int to[32]; bool Main() { cin >> n >> W; if(n == -1 && W == -1) return 0; for (int j=0; j<=31; ++j) { w[j].clear(), v[j].clear(), to[j] = 0; for (int i=0; i<=1000; ++i) f[j][i] = 0; } for (int j=1; j<=n; ++j) { int ww, vv; cin >> ww >> vv; for (int i=30; ~i; --i) { if(ww % (1 << i) == 0) { w[i].push_back(ww / (1 << i)); v[i].push_back(vv); break; } } } for (int j=30, curW=0; ~j; --j) { bool flag = 0; to[j] = to[j+1] * 2; if(W & (1<<j)) flag = 1, to[j] ++; if(to[j] <= 1000) { for (int i=0; i<=to[j]; ++i) if(flag == 0) { if(i%2 == 0) f[j][i] = f[j+1][i/2]; } else if(i%2 == 1) f[j][i] = f[j+1][i/2]; } else { for (int i=0; i<=1000; ++i) if(flag == 0) { if(i%2 == 0) f[j][i] = f[j+1][i/2]; } else if(i%2 == 1) f[j][i] = f[j+1][i/2]; for (int i=500; i<=to[j+1]; ++i) f[j][1000] = max(f[j][1000], f[j+1][i]); to[j] = 1000; } for (int k=0, kto = w[j].size(); k<kto; ++k) { for (int i=0; i<=to[j]; ++i) { int tw = w[j][k], tv = v[j][k]; if(i - tw >= 0) f[j][i-tw] = max(f[j][i-tw], f[j][i]+tv); } } } ll ans = 0; for (int i=to[0]; ~i; --i) ans = max(ans, f[0][i]); cout << ans << endl; return 1; } int main() { while(Main()); return 0; }