当前位置:网站首页>[ACNOI2022]物品
[ACNOI2022]物品
2022-08-01 16:32:00 【OneInDark】
题目
题目背景
当你感到疑惑时,不妨向神明祈祷;神可以回答一切问题。
O n e I n D a r k \sf OneInDark OneInDark:“卷爷为什么这么卷啊?”
O U Y E \sf OUYE OUYE:“没有比这更质朴的快乐了。”
D D G \sf DDG DDG:“天天说别人二次元,有意思没有嘛。”
O U Y E \sf OUYE OUYE:“这难道不令你感到舒适吗?”
最后是『神明三连击』:
c r a s h e d \sf crashed crashed:“哦,忘记给你说了……”
Tiw-Air-OAO \textsf{Tiw-Air-OAO} Tiw-Air-OAO:“你看到的只是表象……”
O U Y E \sf OUYE OUYE:“本质是一样的!”
题目描述
重量为 k ( − m ⩽ k ⩽ m ) k\;(-m\leqslant k\leqslant m) k(−m⩽k⩽m) 的物品有 a k a_k ak 个,求重量和恰为 L L L 的最多物品数,或报告无解。
数据范围与提示
m ⩽ 200 m\leqslant 200 m⩽200 但 ∣ L ∣ ⩽ 1 0 18 |L|\leqslant 10^{18} ∣L∣⩽1018 以及 a k ⩽ 1 0 12 a_k\leqslant 10^{12} ak⩽1012 。
思路
背包问题的最好优化方式是 balance \textit{balance} balance 。这个在子集和问题的论文中有说到。
简单来说,可以让背包重量始终在 L L L 附近徘徊。因此我们可以略去很多无用信息。
与这道题也比较像,考虑我们需要怎样的调整。但套用那样的方法,得不到好的结果,因为权值与重量并不挂钩。
怎么让二者挂钩呢?性价比。所以我们的贪心初解按照性价比排序,然后这题就做完了!然而我就是想不到,悲夫。
完整地说一遍:先做初步转化,将重量为负数的物品先选上,则其权值变为 − 1 -1 −1,即 “退回” 性价比为 − 1 w \frac{-1}{w} w−1 。按照性价比排序,即先选择重量为 1 , 2 , … , m 1,2,\dots,m 1,2,…,m 的物品,再 “退回” 重量为 − m , − ( m − 1 ) , … , − 1 -m,-(m{-}1),\dots,-1 −m,−(m−1),…,−1 的物品。
考虑最优解与其的差距。存在 balance \textit{balance} balance 方式,即总重量在 [ L − m , L + m ] [L{-}m,L{+}m] [L−m,L+m] 内摇摆;若经过若干次操作后,总重量不变,该替换会使得性价比变低(否则贪心初解可以更优),因此是无意义的。
因此前缀和两两不同,即最多有 2 m 2m 2m 次调整。因此调整量不超过 2 m 2 2m^2 2m2,每个物品也只需考虑 2 m 2m 2m 以内的增减变化,拿出来做个部分背包即可。
时间复杂度 O ( m 3 ) \mathcal O(m^3) O(m3),或者二进制分组 O ( m 3 log m ) \mathcal O(m^3\log m) O(m3logm) 。
代码
#include <cstdio>
#include <algorithm> // Almighty XJX yyds!!
#include <cstring> // oracle: ZXY yydBUS!!!
#include <cctype> // rainybunny root of the evil.
#include <cstdint>
using llong = long long;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
for(; isdigit(c); c=getchar()) a = a*10+(c^48);
return a*f;
}
const int MAXN = 300;
llong posi[MAXN+1], nega[MAXN+1];
const int MAXM = MAXN*(MAXN<<1);
const llong INF = (1ll<<60)-1;
llong dp[MAXM<<1|1];
void knapsack(const int& cnt, int weight, int value){
if(cnt == 0) return;
for(int j=0,w,v; true; ++j){
if((cnt>>j) == 1){
w = weight*(cnt^(1<<j))+weight;
v = value*(cnt^(1<<j))+value;
}
else w = weight<<j, v = value<<j;
if(w < 0) rep(i,-w,MAXM<<1)
dp[i+w] = std::max(dp[i+w],dp[i]+v);
else drep(i,MAXM<<1,w) // positive
dp[i] = std::max(dp[i],dp[i-w]+v);
if((cnt>>j) == 1) break;
}
}
inline int trim(const llong& v){
return v >= (MAXN<<1) ? (MAXN<<1) : int(v);
}
int main(){
int n = readint();
llong L; scanf("%lld",&L);
drep(i,n,1) scanf("%lld",&nega[i]);
llong ans; scanf("%lld",&ans);
rep(i,1,n) scanf("%lld",&posi[i]);
if(L < 0){
// negate everything
rep(i,1,n) std::swap(nega[i],posi[i]);
L = -L;
}
rep(i,1,n) L += nega[i]*i;
std::fill_n(dp,MAXM<<1|1,-INF);
dp[MAXM] = 0; // known
rep(i,1,n){
// greedily use them
llong cnt = std::min(L/i,posi[i]);
L -= i*cnt, ans += cnt;
knapsack(trim(cnt),-i,-1);
knapsack(trim(posi[i]-cnt),i,1);
}
drep(i,n,1){
llong cnt = std::min(L/i,nega[i]);
L -= i*cnt, ans += nega[i]-cnt;
knapsack(trim(cnt),-i,1);
knapsack(trim(nega[i]-cnt),i,-1);
}
if(L < MAXM && ans+dp[L+MAXM] >= 0)
printf("%lld\n",ans+dp[L+MAXM]);
else puts("impossible");
return 0;
}
边栏推荐
猜你喜欢
随机推荐
【建议收藏】技术面必考题:多线程、多进程
PAT 甲级 A1003 Emergency
提速!进口婴幼儿配方产品出证仅需1-3天
怎么安装汉化包(svn中文语言包安装)
探讨if...else的替代方案
请问数据库中报错信息如下,mongoshake 有什么配置的方式解决这种大消息问题吗?
Go unit tests
5年测试,只会功能要求17K,功能测试都敢要求这么高薪资了?
90后的焦虑,被菜市场治好了
MUI 做手机返回操作栏
js邯郸市地图网页源码下载
MySQL INTERVAL Keyword Guidelines
06 redis cluster structures
DOM series of touch screen events
Shell basic function writing
Winform message prompt box helper class
珠海市生物安全P3实验室主体结构封顶
Synchronized原理
【无标题】
Vulnhub target drone: HARRYPOTTER_ NAGINI









