AtCoder Grand Contest 020 C - Median Sum(背包+bitset)
题目链接
题目大意
给你
个数,
。
且
为A的所有非空子集。 问S的中位数是多少。
数据范围
解题思路:
这道题比赛的时候没做出来, 后来补的,, 网上搜了一下题解发现只有两个人写了。。 并且只写了背包+bitset, 并没写怎么理解的。。。 看来是我太菜了。 然后我就去翻了一下官方题解和油管的讲解视频(日语的听不懂)。 大体猜了一下
问题主要有两个:
1. 为什么要使用bitset。
2. 使用了bitset处理了之后如何快速获得中位数。
首先是为什么要用bitset, 用bitset第x位表示x是否出现, 主要作用是快速的得到之前的所有状态经过现在更新之后能得到的所有状态。 就是代码中
dp |= dp << x;
dp就是前一个bitset状态, 这句代码的意思就是先将之前的dp 全部左移x位(表示之前所有的集合都加上x形成的所有新集合)再 或上之前dp的状态(表示不加上x之前所有集合的原状态)。 这样两个一异或, 对于x取和不取所有的状态都能用dp这个bitset表示出来。如何找到中位数, 这个地方我没办法证明。。 只能当做一个性质或者一个技巧记下来了。 就是A数组所构成的所有集合S (包括空集) 一共是 个, 这些集合按照从小到大排列之后, 头和尾一一对应相加都等于 。(油管官方视频解释的。。 但是日语我听不懂) 这样只需要从 往后找到第一个出现的数, 即为答案。更新: 如何找到中位数, 首先对于任何一个集合A, 我们都能找到他的一个对应的补集, 并且这个补集也是S的子集嘛, 这样一来, S的所有子集从小到大排序, 头尾一一相加必定为 。 那么 往后出现的一个数就是中位数。
附上图片:
AC代码:
/********************************************
*Author* :ZZZZone
*Created Time* : 日 1/14 22:02:31 2018
* Ended Time* : 四 1/18 13:11:34 2018
*********************************************/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
#include <bitset>
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
typedef unsigned long long ULL;
const int MaxN = 2000;
int n;
int main()
{
while(~scanf("%d", &n)){
bitset<MaxN * MaxN> dp;
dp[0] = 1;
int sum = 0;
for(int i = 1; i <= n; i++){
int x;
scanf("%d", &x);
sum += x;
dp |= dp << x;
}
for(int i = (sum+1)/2; ; i++){
if(dp[i]) { printf("%d\n", i); break;}
}
}
return 0;
}