题目大意
给出 n ( n ≤ 100 ) n( n \leq 100) n(n≤100)个数的序列 a i ( 1 ≤ a i ≤ 2000 ) a_i(1 \leq a_i \leq 2000) ai(1≤ai≤2000),若这个序列能被分成两部分且这两部分的和相等,称这个序列是不好的。最少删除几个数使得这个序列不是不好的。
解题思路
不难想到,若一个序列能分为两部分和相同的,那么所有数的和 s u m sum sum一定是偶数,然后如何判断初始的序列能否分成两部分呢?背包,每个物品选或者不选,判断最后能否凑成 s u m 2 \frac{sum}{2} 2sum。
若能凑成,需要判断删除几个数,直觉判断删除一个数就可以(这部分无法证明,题解也没说如何证明),但这个数不能随便删,考虑序列是否有奇数:
-
若序列至少有一个奇数,我们删除这个奇数后剩下序列的和一定是奇数,一定不是不好的序列。
-
若序列全是偶数,注意到两部分存在一个求和的等式,对这个等式两边不断除以2,不影响“相等的结果”,因此只需要删除含有2的幂次最少的数。
#include <bits/stdc++.h>
using namespace std;
#define ENDL "\n"
typedef long long ll;
const double eps = 1e-8;
const int inf = 0x3f3f3f3f;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;
struct node {
int id, val;
int cnt;
} b[105];
bool cmp(node &p, node &q) {
return p.cnt < q.cnt; }
int a[105];
bool d[105][maxn];
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt", "w", stdout);
// ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, sum = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i], sum += a[i];
b[i].id = i;
b[i].val = a[i];
}
if (sum & 1) {
cout << "0" << ENDL;
return 0;
}
int m = sum / 2;
d[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
d[i][j] = d[i - 1][j];
if (j >= a[i]) d[i][j] |= d[i - 1][j - a[i]];
}
}
if (d[n][m]) {
for (int i = 1; i <= n; i++) {
if (a[i] & 1) {
cout << 1 << ENDL << i << ENDL;
return 0;
}
}
for (int i = 1; i <= n; i++) {
int x = b[i].val;
while (x % 2 == 0) {
x /= 2;
b[i].cnt++;
}
}
sort(b + 1, b + 1 + n, cmp);
cout << 1 << ENDL;
cout << b[1].id << ENDL;
} else
cout << 0 << ENDL;
return 0;
}