C. Baby Ehab Partitions Again
原题链接:传送门
人一我十, 人十我百,追逐青春的梦想,怀着自信的心,永不言弃!
题目大意
给定一个数组,要求让我们拆分成两个子序列,且保证这两个子序列的和不相等,问最少移除多少个元素,可以满足以上条件,如果不满足则输出 0
分析
1. 当出现两种情况满足或者不满足的时候首先我们应该分析一下什么时候可能满足,什么时候一定不满足情况
1. 不用删除数字的情况
- 1.1 由于是拆分成两个子序列,如果要保证这两个子序列的和相等那么这两个子序列的和加起来一定是一个偶数(即整个数组的和为偶数) 所以如果数组的和为奇数的情况那么就不用删除任何元素
- 1.2 虽然数组的和为偶数,但是不存在一种方案满足 子序列和 = (sum / 2) 这一步我们需要利用01背包求方案数来解决,只需要检查sum / 2 是否存在即可
2. 需要删除数字的情况
首先需要删除数字的话一定是和为偶数,并且存在一个奇数
- 2.1 如果序列中存在一个奇数的话,删除这个奇数那么一定可以使得整个数组的和边为奇数
但是如果整个数组的和为偶数,并且每一个数字都是偶数。那么此时没有奇数可以移除,怎么办呢?
由于我们只需要满足整个序列的和偶数,那么如果我们对全体元素除以2 此时不影响整体和的奇偶性,直到满足序列中至少存在一个奇数为止。
因为序列的和整体为偶数,那么对这个和除以2这个序列的和依旧为偶数,不改变整个序列和的奇偶性。数学也太菜了哭了
AC 代码
AC code
const int MAX = 0x7ffffff;
const int MIN = 0xcf;
const int N = 2 * 1e5 + 5;
int test;
int f[N];
void slove()
{
int n;
cin >> n;
vector<int> a(n + 1);
for(int i = 1; i <= n; i++)
cin >> a[i];
while(1)
{
int cando = 1;
// 检查序列中是否出现奇数
for(int i = 1; i <= n; i ++)
{
if(a[i] % 2 == 1)
{
cando = 0;
}
}
// 如果出现了奇数就可以向下继续
if(!cando) break;
// 让每一个数字每一次都除上2
for(int i = 1; i <= n; i ++)
a[i] >>= 1;
}
int s = 0;
for(int i = 1; i <= n; i++) s += a[i];
// 如果和为奇数 那么序列一定不可能拆分成两个子序列和相同
if(s % 2 == 1)
{
cout << 0 << endl;
return;
}
// 否则检查 sum / 2 是否出现了
f[0] = 1;
for(int i = 1; i <= n; i ++)
for(int j = s; j >= a[i]; j --)
f[j] |= f[j - a[i]];
// 没有出现表示不会组成这种方案
if(!f[s / 2])
{
cout << 0 << endl;
return;
}
// 否则任意去掉一个奇数即可
int pos = 0;
for(int i = 1; i <= n; i++)
{
if(a[i] % 2 == 1)
{
pos = i;
break;
}
}
cout << "1
" << pos << endl;
}