题面
题目描述
MM 虽然一辈子只要一个,但是也得早点解决。于是,n 个光棍们自发组成了一个光棍组织 (ruffian organization,By Wind 乱译)。现在,光棍们打算分成几个小组,并且分头为 找 MM 事
业做贡献(For example:searching,hunting……By Wind 乱译)。 对于这 n 个光棍的任意一个组合,都有一个被称为“和谐度”的东西,现在,他们想知道, 如何分组
可以使和谐度总和最大。 每个光棍都必须属于某个分组,可以一个人一组。
输入格式
第 1 行为 n,接下来 2^n-1 行,按照 2 进制给出每个分组的和谐度。
(比如接下来第 5 行,也就是总共第六行,2 进制为 00000101,则表示第 1 个人和第 3 个人 这个分组的和谐度,第 31 行则为 1~5 在一起的和谐度)
题解
这道题目是一个子集DP,每一个子集可以用一个n位二进制数表示。对于任意一个集合i都是由它的子集任意组合形成的。
状态
我们设 f[i] f [ i ] 代表状态为i的最大和谐度。
状态转移方程
f[i]=max{f[j]+f[i−j]} j是i的子集 f [ i ] = m a x { f [ j ] + f [ i − j ] } j 是 i 的 子 集
code
#include<bits/stdc++.h>
using namespace std;
inline long long read(){
long long num=0;
char c=' ';
bool flag=true;
for(;c>'9'||c<'0';c=getchar())
if(c=='-')
flag=false;
for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
return flag ? num : -num;
}
const int maxn=18;
int n;
long long f[1<<maxn];
void init(){
n=read();
for(int i=1;i<1<<n;i++)
f[i]=read();
}
void dp(){
for(int i=1;i<1<<n;i++)
for(int j=(i-1)&i;j;j=(j-1)&i)
//非常经典,枚举子集
f[i]=max(f[i],f[j]+f[i-j]);
printf("%lld\n",f[(1<<n)-1]);
}
int main(){
init();
dp();
return 0;
}