[枚举][dp]JZOJ P3601——Tree

Description

下图展示了一种二叉树:

这里写图片描述

这种二叉树的每个叶子节点上都标注了权值,而且具有以下有趣的特性:每个非叶子节点,其左右子树叶子节点的权值之和相等。我们称这种二叉树叫平衡二叉树。

我们将一棵平衡二叉树叶子节点的权值从左到右列出来,假如这个权值序列是另一个序列A的子序列,我们称这棵平衡二叉树“隐藏”在序列A当中。在本题中,我们称一个序列S2是另一个序列S1的子序列,当且仅当S2可以由S1中删除0个或多个元素,但不改变S1中剩余元素的相对位置获得。

例如,上图中的平衡二叉树隐藏在序列3 4 1 3 1 2 4 4 6中,因为4 1 1 2 4 4是该序列的子序列。

你的任务是对给定的整数序列,寻找当中隐藏的具有最多叶子节点的平衡二叉树。

Input

输入的第一行是一个正整数n(n<=1000),分别序列的元素数目,第二行是用空格分隔的n个整数ai(1<=i<=n,1<=ai<=500)代表了该序列的第i个元素。

Output

输出只有一个整数,代表该序列当中隐藏的具有最多叶子节点的平衡二叉树的叶子节点总数。

Sample Input

输入1:

9

3 4 1 3 1 2 4 4 6

输入2:

4

3 12 6 3

输入3:

10

10 9 8 7 6 5 4 3 2 1

输入4:

11

10 9 8 7 6 5 4 3 2 1 1

输入5:

8

1 1 1 1 1 1 1 1

Sample Output

输出1:

6

输出2:

2

输出3:

1

输出4:

5

输出5:

8

Data Constraint

n<=1000,1<=ai<=500

题解

易得,如果一个数满足是该树里的叶子节点,必须是2^x*y
所以,我们可以先将2的1~9次幂求出来
然后枚举一个因数k(1~500)
设f[x]为和为x的可得的最长的子序列
最后f[x]=max(f[x],f[x-ai]),这条方程式在ai|x的时候可以转移

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=500005;
int n,f[N],a[N],b[N],bz[N];
bool two[510];
int main()
{
    int x=1,ans=0;
    for(int i=0;i<=9;i++) two[x]=1,x*=2;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int k=1;k<=500;k++)
    {
        memset(f,0,sizeof(f));
        memset(bz,0,sizeof(bz));
        int m=0,sum=0;
        for(int i=1;i<=n;i++) if(a[i]%k==0) b[++m]=a[i]/k,sum+=b[m];
        for(int i=1;i<=m;i++)
            if(two[b[i]])
            {
                for(int j=(sum/b[i])*b[i];j;j-=b[i]) if(f[j]) f[j+b[i]]=max(f[j+b[i]],f[j]+1);
                f[b[i]]=max(f[b[i]],1);
            }
        for(int i=1;i<=sum;i<<=1) ans=max(ans,f[i]);
    }
    printf("%d",ans);
    return 0;
}
posted @ 2018-01-29 18:46  BEYang_Z  阅读(164)  评论(0编辑  收藏  举报