1 条题解
-
0
自动搬运
来自洛谷,原作者为

AFewSuns
弱省弱校蒟蒻,tcl,zbl搬运于
2025-08-24 22:58:53,当前版本为作者最后更新于2024-05-23 20:06:58,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
背包板子,来水一篇题解。
题目大意
给定一个长度为 的数组 ,每次可以选定两个数,若这两个数相同则同时删掉;否则将较小值删掉,然后将较大值修改为它们的差。问最后剩下的数的最小值。
。
题目分析
先进行一些简单的转化。不妨允许存在负数,那么最终每个数的贡献都会是 或 ,需要最小化的则是最后的和的绝对值。
具体而言,我们的操作可以看作是每次选择一个数 去跟当前的数 进行“抵消”。若 则将 减去 ;若 则将 加上 。
这乍一看会有加入顺序导致的 限制,但其实我们完全可以将它看成任意选取 的系数后最小化和的绝对值。这是为什么呢?
设 为选取 的数的集合, 为选取 的数的集合,那么可以在当前数 的时候选取 内的数进行减法,在当前数 的时候选取 内的数进行加法,就完全符合操作的限制了。
如果最后发现某个集合空了,不妨假设是 空了,那么在当前 的时候就不得不使用 的数了。但这显然不优,因为把这些数的系数换成 即可使得最后的绝对值减小。
设 ,将整体加上 再除以二就变成了 背包问题,需要在和 的同时最大化和。
设 最大值为 ,minstdfx 的做法是随机打乱后 bitset 优化 01 背包 。
实际上有 的做法,具体做法在这里,或者参考 ABC221G 的题解。
具体地,先贪心从前往后选取一段前缀的物品使得他们的和刚好 ,设选取的前缀为 ,然后将序列分为 和 两部分。
考察最后选取的物品,一定是从当前的状态下,删去 的一些物品,再加上 的一些物品得到的。注意到可以通过合理安排顺序使得过程中的和始终在 中间。
于是设 表示最大的 ,使得仅操作 内的数可以凑成 。这里操作指的是删去 中的数或加入 中的数。
转移分为三类:
-
,即从 转移过来且不操作 ;
-
$f_{r,w+a_r} \leftarrow f_{r-1,w}(w \leq \frac{sum}{2})$,即从 转移过来且操作 ;
-
$f_{r,w-a_l} \leftarrow l(w > \frac{sum}{2},l < f_{r,w})$,即从左边转移过来且操作 。
注意第三步转移中,若 ,那么这种情况会在 时被转移(之后通过第一步转移回来),所以只需要转移 的部分,单次转移复杂度是 ,所以总的复杂度是对的。由于第二维的范围在 内,是 的,所以总时间复杂度为 。
代码
#include<bits/stdc++.h> using namespace std; using namespace my_std; ll n,m,all=0,V=5000,a[10010],f[2][10010]; int main(){ n=read(); fr(i,1,n) a[i]=read(); fr(i,1,n) all+=a[i]; m=all/2; ll pos=0,sum=0; while(pos<n&&(sum+a[pos+1])<=m){ pos++; sum+=a[pos]; } f[pos&1][sum-m+V]=pos+1; fr(i,pos+1,n){ ll o=i&1; fr(j,1,2*V) f[o][j]=0; fr(j,1,2*V) f[o][j]=max(f[o][j],f[o^1][j]); fr(j,1,V) f[o][j+a[i]]=max(f[o][j+a[i]],f[o^1][j]); pfr(j,2*V,V+1) fr(k,f[o^1][j],f[o][j]-1) f[o][j-a[k]]=max(f[o][j-a[k]],k); } pfr(i,V,1){ if(f[n&1][i]){ write(all-2*(m+i-V)); return 0; } } } -
- 1
信息
- ID
- 10266
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者