题目
思路
直接背包肯定是不行滴,背包容量都快到
极限了。除非你有欧洲血统。
去网上搜搜题解,发现了爆搜做法,并表示很淦。再找找,发现了合理的背包做法。然后把他俩的结合起来,得到了当前做法。
给出如下性质:从轻到重考虑,在能够选择的前提下,每个物品最多有 个不选!
为啥?可以这样考虑,假如我一股脑塞进背包,可能不是最优情况,因为背包的一部分剩余空间没有被充分利用。比如,在重量为 时,背包已经只剩下不到 的空间了。
假如要放入重量为 的物品 个,拿出重量为 的物品 个,导致背包总使用量增大 ,其实就是方程
改成同余方程组就是
显然在 时, 可以取遍所有可能的余数。即:若方程有解,则存在一解使得 。
根据上一条推出 ,故 ,所以 即 。
此时我们已证 且 。故,在 个物品以内的调整,就足以满足所有要求。所以我之前的每种重量留下 个备用即可!不到 个?我全都留下来呗,你还要我怎样?
除了备用重量,其他的一股脑往背包里塞,求出剩余容量,然后对剩余物品进行背包。显然剩余容量最多用到 ,复杂度完全不用担心呢,即使不用单调队列(或二进制分组)优化,也可以很快的跑过!
代码
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
inline int_ readint(){
int_ a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MaxL = 8; // 剩下几个
const int MaxW = MaxL*(8*9>>1);
bool dp[MaxW+1]; // 剩余容量的背包
long long w, ans, cnt[9];
int main(){
w = readint();
for(int i=1; i<9; ++i){
cnt[i] = readint();
if(cnt[i]*i > w)
cnt[i] = w/i;
long long lqy = cnt[i]-MaxL;
if(lqy < 0) lqy = 0;
ans += lqy*i, w -= lqy*i;
cnt[i] -= lqy;
}
dp[0] = true; if(w > MaxW) w = MaxW;
for(int i=1; i<=8; ++i)
for(int j=w; j; --j) if(!dp[j])
for(int k=1; k*i<=j&&k<=cnt[i]; ++k)
if(dp[j-k*i]){ dp[j] = 1; break; }
for(int j=w; ~j; --j) if(dp[j]){
printf("%lld",ans+j); break;
}
return 0;
}