原题链接:https://atcoder.jp/contests/agc020/tasks/agc020_c
上来先给AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5;
bitset<N>V;
int main()
{
int n,s=0;
scanf("%d",&n);
V[0]=1;
for(int i=0,x; i<n; i++)scanf("%d",&x),s+=x,V|=V<<x;
for(int i=s>>1;;i--)
if(V[i])
{
printf("%d\n",s-i);
return 0;
}
}
相信大部分人看不懂的部分在于那个V|=V<<x;语句,这里我给大家详细讲解下。
首先我们定义子集的权值=子集所有元素之和
再定义bitset数组,bit【any】=1,就代表存在一个子集,他的权值为any。bitset数组不懂的可以去百度一下。
bitset[0]=1;他的含义是这个集合存在一个子集,这个子集的权值为0(虽然题目没有空子集,但为了运算简便我们还是将其定义出来),bitset[x]=1的含义是,权值为x的子集存在。
- 现在,加入一个数x,可以看到原先集合的子集为{∅},现在的集合为{x},子集为{∅},{x}。
- 那么现在子集的权值分别为0,x。可以看出来,多出来的权值是在原先权值的基础上加新加入的数x。(看不懂不用急,等下还会有例子)
- 那么我们队bitset数组进行移位操作,因为bitset[0]=1,所有左移x位后bitset[x]=1,代表这现在集合中存在一个子集,它的权值为x
- 再加入一个数y,之前所有子集的权值分别为0,x。现在新的集合的所有子集的权值分别为,0,x,0+y,x+y。即新的子集的权值是在原子集的权值基础上加上数y的。那么对bitset进行左移y位的操作,因为bitset[0]=1,bitset[x]=1,所有bitset[0+y]=1,bitset[x+y]=1。这就代表着存在子集,他们的权值分别为y与x+y。
- 但是我们不能忘记,之前的子集{∅},{x}依然存在,所有我们将左移后的结果与原先的bitset数组进行或运算,这样子的话bitset[0],bitset[x],bitset[0+y],bit[x+y]的值就都为1了。意味着该集合的所有子集的权值分别为0,x,y,x+y;
所以我们就能得出所有子集的权值,并将其存储在bitset数组上。
接下来是寻找中位数, 首先对于任何一个集合A, 我们都能找到他的一个对应的补集,这个补集也是B集合中的一个子集,sum=B集合所有元素之和。sum/2后出现的一个数就是中位数(因为空集合是不存在的)