CF1516C Baby Ehab Partitions Again (01背包 + 思维)

题目链接: Baby Ehab Partitions Again

2021.6.3 更新了一些关于最后删法的说明.

大致题意

给出一个长度为n的序列, 如果我们能把这个序列分成两部分, 使得两部分的元素和相等, 则认为序列是不好的.

要求: 删除尽可能少的数字, 使得序列变成好序列.

解题思路

01背包 + 思维

我们首先从整体的角度考虑一下, 如果这个序列本身就是好的, 有两种情况:
情况①: 序列的所有元素和为奇数, 那么我们一定凑不成相等的两部分.
情况②: 序列的所有元素和为偶数, 但是无法分成相等的两部分.

对于情况①而言, 我们直接统计元素和判断即可.
对于情况②而言, 假设所有元素和为sum, 我们需要判断能否凑出 sum/2. 若可以, 则序列一定不满足要求(不好的).


凑元素的过程: 对于一个元素而言, 我们只有选或者不选两种情况. (有没有感觉DNA起反应了? 是不是有01背包的感觉?)
我们需要用类似01背包的思路, 去看看当枚举到第i个元素时, 记录所有能凑出的元素和的情况.

当到这一步, 如果我们不能凑出sum/2, 输出0结束!


如果凑出了, 我们又如何删除元素呢?

比较简单的一个思路是, 如果我们使得删除后的序列, 变成情况①, 不就可以了?
那么已知我们当前的序列和是偶数了, 如果删除一个奇数元素, 一定成立.


但问题又来了, 如果没有奇数元素呢?

说明所有的元素都是偶数, 我们给所有元素都除以2, 一定是和之前的选法一一对应的.
此时如果有奇数了, 我们删除奇数一定是成立的, 反之, 我们继续除2计算即可.

我们为什么多次除2之后, 序列删除此时的某个奇数一定成立? (下文的除2都指可以整除的情况)

我们可以反过来看, 首先明确, 除不除2, 是不影响序列的选择方式的.
假设我以当前的选择方式, 可以证明这个序列是不好的. 那么无论我把这个序列整体 乘2 或 除2, 它也都是不好的. 因为如果我保持以之前的方式去选择, 最后两边的加和仍是相同的.

我们如果明白了这一点, 我们就可以认为原序列 与 原序列无数次除二后得到的新序列是完全等价的.

那么当我从新序列移除掉那个奇数元素, 可以证明新序列一定变成了好序列, 因为符合情况①, 因此新序列此时不存在一种选法, 使得可以证明新序列不是好序列.

因此得证: 我们移除原序列多次除2后的奇数数字, 一定可以使得原序列变好.

这里再给大家提供一种证明: 我们从的角度去分析:
如果我们能把序列分成相等的两部分, 那么他们二进制的形式, 每一位上的1的个数都是相等的.
我们每次除2 或者 乘2, 并不会改变原本相等的1的数量, 只是可能将他们向左或向右移动了.
因此当我们无数次除2后, 移走此时最低位是1的数字(那个奇数), 此时一定会破坏这种平衡性.

如果你此时又有了想法, 那我拿别的位置的1可不可以呢? 答案是有可能也是可以的, 但是你要考虑到, 当我们拿走了某一个高位1后, 有可能会存在一种新的组合方式, 使得用相应个低位的1, 替代了这个高位的1.

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 110, M = 2E3 + 10;
int a[N];
bool dp[M * N];
int main()
{
	int n; cin >> n;
	int sum = 0; //统计元素和
	rep(i, n) scanf("%d", &a[i]), sum += a[i];

	if (sum & 1) return printf("0\n"), 0; //情况①

	dp[0] = 1; //显而易见, 什么都不选的情况, 即0是一定可以凑出的.
	rep(i, n) {
		for (int j = sum; j >= a[i]; --j) {
			dp[j] |= dp[j - a[i]];
		}
	}

	if (!dp[sum / 2]) return printf("0\n"), 0; //情况②

	int d = 0; //求一下整个序列的gcd, 因为多次除二, 和除一次gcd是一样的效果.
	rep(i, n) d = gcd(d, a[i]);

	rep(i, n) if (a[i] / d % 2 == 1) return printf("1\n%d\n", i), 0;
	return 0;
}

END

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值