输赢是次要的,我们并肩作战才是最重要的!——坎公骑冠剑

Solution - Median Sum

其它题不是很写得动了跑来写一下这个题,还是挺有趣的。

给定由 n 个正整数 a1,a2,,an 组成的可重集合,求出它的非空子集的和的中位数。

sum=i=1nai

首先是对于任意一个子集,设其和为 x,我们将其取反,就是选的改成不选,不选的改成选,那么前后子集之和是 sum,改之后的子集和就是 sumx,不妨 xsumx,那么一定有 xsum2,sumxsum2。如果这时候把空集也算进去(方便计算,而中位数取中间更大的那个即可,也就是从小到大排序之后位于 2n2+1=2n1+1 位置的数),和 sum2 的子集个数就一定为 2n1。于是我们只需要找到第一个 >sum2 的子集和就好了。

于是就转化成了一个存在性问题。因为这个 n,ai2000,考虑直接设 fi,j 表示前 i 个数的和能否是 j。转移不表,但是这样时空复杂度都是 O(n3) 的。滚动数组空间可以优化到 O(n2),至于转移可以发现就是一堆或的操作,用 bitset 维护,为 O(n3w),就可以过了。

namespace liuzimingc {
const int N = 2e3 + 5, M = 4e6 + 5;

int n, a[N], sum;
bitset<M> f[2];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i], sum += a[i];
	f[0][0] = true;
	for (int i = 1; i <= n; i++) {
		f[i & 1] = f[i - 1 & 1]; // 不选第 i 个
		f[i & 1] |= f[i - 1 & 1] << a[i]; // 选第 i 个
	}
	for (int i = sum + 1 >> 1; ; i++) // >= sum / 2,隐含向上取整
		if (f[n & 1][i]) return cout << i << endl, 0;
	return 0;
}
} // namespace liuzimingc

附赠一下,比如今天的 Eighty seven,乍一看以为是什么高级东西,但是发现维护的东西跟上面这个都是一样的,也这么做就好了。

posted @   liuzimingc  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示