2014新生暑假个人排位赛03 E. 学姐的数列

本文介绍了一种算法,用于解决给定一个由2的幂次表示的数列,如何找出其中最长的能够构成平衡二叉树的子序列问题,并提供了具体的实现代码。
时间限制 5000 ms   内存限制 65536 KB

题目描述

定义一颗平衡二叉树满足条件:对于任意节点,其左子树的权值之和等于右子树权值之和。如图:

我们用序列表示上图平衡二叉树,即4 1 1 2 4 4。
现在给定一个序列,求其能最长的能构成平衡二叉树的子序列。子序列元素为原序列的子集,且元素间保持原顺序。

输入格式

输入第一行为数据组数 T(T10) ,接下来 T 组数据,每组第一行 n(1n128) 为序列元素个数,下一行给出 n 个正整数, ai(ai7) 表示第 i 个数为2^ ai

输出格式

每组数据输出一行,符合题意的最长子序列的长度。

输入样例

2
6
2 0 0 1 2 2
5
0 0 0 0 0

输出样例

6
4
n^3 dp暴力,设dp[i][j][w]为区间[i,j]上和为2^w的最大长度,不难得出,dp[i][j][w]=max(dp[i][k][w-1]+dp[k+1][j][w-1])(i<=k<j),初始化为dp[i][j][a[t]]=1,(i<=t<=j)。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <set>
#include <map>
#include <stack>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
 
using namespace std;
 
typedef long long ll;
typedef unsigned long long ull;
 
const int maxn = 140;
 
int dp[maxn][maxn][20];
 
int main()
{
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
    //freopen("output.txt", "w", stdout);
#endif
 
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n;
        int a[maxn];
        scanf("%d", &n);
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
        }
 
        memset(dp, 0, sizeof(dp));
 
        for(int i = 0; i < n; i++)
            for(int j = i; j < n; j++)
                for(int k = i; k <= j; k++)
                {
                    dp[i][j][a[k]] = 1;
                }
 
 
 
        for(int k = 1; k < 15; k++)
            for(int i = 0; i < n; i++)
                for(int j = i+1; j < n; j++)
                    for(int t = i; t < j; t++)
                    {
                        if(!dp[i][t][k-1]||!dp[t+1][j][k-1])//这个判断不能省!
                            continue;
                        if(dp[i][j][k] < dp[i][t][k-1] + dp[t+1][j][k-1])
                            dp[i][j][k] = dp[i][t][k-1] + dp[t+1][j][k-1];
                    }
 
        int ans = 0;
 
        for(int k = 0; k < 15; k++)
        {
            ans = max(ans, dp[0][n-1][k]);
        }
 
        printf("%d\n", ans);
 
    }
 
 
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值