题目大意:
对于一个集合S={1,2,3,……,n},他有2^n个子集。对于每个子集T,我们给定他的得分为F(T)。
现让你寻找一个S的子集T,使得T的所有子集的得分和尽可能大。
输入数据第一行有一个正整数n。
随后的2^n行,按“二进制序”从小到大给出每个子集的得分。
例如n=3时,就是按照{}、{1}、{2}、{1,2}、{3}、{1,3}、{2,3}、{1,2,3}的顺序给出的。
//smoj 1707
solution:
O(nLogn)
用F[I][J]表示编号为I的集合,其标号后J位通过变化可取得的所有子集的分数和
f[1101][0]=v[1101]
f[1101][1]=v[1100]+v[1101]
f[1101][2]=v[1100]+v[1101]
f[1101][3]=v[1000]+v[1001]+v[1100]+v[1101]
f[1101][4]=v[1000]+v[1001]+v[1100]+v[1101]+v[0000]+v[0001]+v[0100]+v[0101]
即:
F[I][J] = F[I][J-1] (I的第J位是0)
= F[I][J-1] + F[I-(1<<(J-1))][J-1] (I的第J位是1)
对于是当前位为0,其子集编号该位显然为0;
如果是1则要讨论是1还是0.
/*
F[I][J]
=F[I][J-1] (I的第j位为0)
=F[I][J-1]+F[I-(1<<(J-1))][J-1] (I的第j位为1)
*/
#include<iostream>
#include<cstdio>
#include<bitset>
#include<cmath>
using namespace std;
int n,res,i,j,m;
int f[1048600][21];
bitset<21> c;
int main()
{
freopen("h.in","r",stdin);
freopen("h.out","w",stdout);
scanf("%d",&n);
m=n; n=1<<n;
for(i=0;i<n;++i)scanf("%d",&f[i][0]);
for(i=1;i<=m;++i)f[0][i]=f[0][0];
for(i=1;i<n;++i)
{
c=i;
for(j=1;j<=m;++j)
{
if(c.test(j-1))f[i][j]=f[i][j-1]+f[i-(1<<j-1)][j-1];
else f[i][j]=f[i][j-1];
}
res=max(res,f[i][j-1]);
}
printf("%d\n",res);
return 0;
}