[DP]集合

来源:互联网 发布:linux和c 编辑:程序博客网 时间:2024/06/06 22:13

题目大意:

对于一个集合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;}