https://codeforces.com/contest/1516/problem/C
输入 n(2≤n≤100) 和长为 n 的数组 a(1≤a[i]≤2000)。
你需要删除 a 中的一些数,使 a 无法分成两个元素和相等的子序列。
输出最少要删除多少个数,以及这些数的下标(从 1 开始)。
注:子序列不要求连续。
输入
4
6 3 9 12
输出
1
2
解释 6+9=3+12,删除 3。
输入
2
1 2
输出
0
分类讨论:
1. 如果 sum(a) 是奇数,显然没法分,无需删除任何数字,输出 0。
2. 如果无法从 a 中选出元素和等于 sum(a)/2 的子序列,那么也没法分,输出 0。这可以用 0-1 背包判断。
3. 否则就可以分,那么要如何删除呢?此时 sum(a) 是偶数,由于偶数 - 奇数 = 奇数,所以减去一个奇数即可。
4. 要是没有奇数呢?此时每个 a[i] 都是偶数,那么把每个 a[i] 都除以 2,是不会影响答案的。反复除以 2 直到 a中有奇数为止。
代码实现时,无需反复除以 2,而是除以最小的 lowbit(a[i])。如果要删除数字,也是删除 lowbit 最小的数。
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n;
int a[N];
int main ()
{
cin >> n;
int sum = 0;
for(int i = 1; i <= n; i ++){
cin >> a[i];
sum += a[i];
}
if(sum & 1){
puts("0");
}
else{
vector<bool> dp(sum / 2 + 1, false);
dp[0] = true;
for(int i = 1; i <= n; i ++)
{
for(int j = sum / 2; j >= a[i]; j --)
{
dp[j] = dp[j - a[i]] | dp[j];
}
}
//cout << dp[sum / 2] << endl;
if(!dp[sum / 2]){
puts("0");
return 0;
}
for(int i = 1; i <= n; i ++)
{
if(a[i] & 1){
puts("1");
printf("%d\n", i);
return 0;
}
}
while(true)
{
int mx = 1e9;
for(int i = 1; i <= n; i ++)
{
mx = min(mx, a[i] & -a[i]);
}
for(int i = 1; i <= n; i ++)
{
a[i] /= mx;
if(a[i] & 1){
puts("1");
printf("%d", i);
return 0;
}
}
}
}
return 0;
}