C. Baby Ehab Partitions Again
恢复训练第一天,记录一道比较简单但是比较有美感的cf题和思路过程。
题意
给出 n n n个数,问最少去掉几个数让他不能选择其中一些数,使得选择的数的和等于剩余的数字和。
想法
首先,容易想到,输出为0的情况是
- 所有数字的总和是奇数,
- 另外一种就是无论怎么选数字都不能得到 s u m 2 \frac{sum}{2} 2sum,这可以通过dp用背包的思想构造。
然后如果输出不为0,数字和必为偶数,一种最简单的想法是找到序列中的一个奇数(如果存在)并且删除。
再然后,就只剩下全为偶数的情况了,乍一看比较难处理,但是仔细想,对于原序列,我集体乘以或者除以一个值,对答案是不影响的,故我们可以通过集体除以二把原序列转化成一个必然存在奇数的序列,从而回到上述讨论中。
代码
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int a[maxn],dp[maxn];
int n;
int main() {
scanf("%d", &n);
int sum = 0;
int div2 = 0x3f3f3f3f;
for (int i = 1; i <= n; ++i) {
scanf("%d", a + i);
int tempa = a[i], pow2 = 1;
while (tempa % 2 == 0) {
tempa /= 2;
pow2 <<= 1;
}
div2 = min(div2, pow2);
}
int flagOdd = 0;
for (int i = 1; i <= n; ++i) {
a[i] /= div2;
sum += a[i];
if (a[i] & 1) flagOdd = i;
}
if (sum & 1)
puts("0");
else {
int V = sum / 2;
for (int i = 1; i <= n; ++i) {
for (int j = V; j >= a[i]; --j) {
dp[j] = max(dp[j], dp[j - a[i]] + a[i]);
}
}
if (dp[V] < V)
puts("0");
else {
printf("1\n%d", flagOdd);
}
}
return 0;
}