P1040 加分二叉树(树上记忆化搜素)

这道题很水

但我没做出来………………………………

我写的时候状态设计错了,设计dp[l][m][r]为从l到r以m为根的值

这样写遍历状态就是n^3的,会TLE。

而且写路径的时候是用结构体写的,这样会错,应该用root[l][r]表示从l到r的根

对于l到r,枚举根在哪就好了

 

总结

(1)状态设计,学会简洁的设计状态

(2)路径输出,可以开和dp数组一样的数组,在dp数组更新的时候路径数组也更新

(3)在非线性结构上做dp的时候(如树),用记忆化搜索会比递推方便。

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++) 
#define _for(i, a, b) for(register int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 50;
int a[MAXN], ans, dp[MAXN][MAXN];
int n, root[MAXN][MAXN];

int dfs(int l, int r)
{
    if(l > r) return 1;
    if(dp[l][r] != -1) return dp[l][r];
    if(l == r) //注意叶子的情况可能要特判 
    {
        root[l][r] = l;
        return dp[l][r] = a[l];
    }
    
    int res = 0; 
    _for(m, l, r)
    {
        int val = dfs(l, m - 1) * dfs(m + 1, r) + a[m];
        if(val > res) res = val, root[l][r] = m;
    }
    
    return dp[l][r] = res;
}

void print(int l, int r)
{
    if(l > r) return;
    printf("%d ", root[l][r]);
    print(l, root[l][r] - 1);
    print(root[l][r] + 1, r);
}

int main()
{
    scanf("%d", &n); 
    memset(dp, -1, sizeof(dp));
    _for(i, 1, n) scanf("%d", &a[i]);
    printf("%d\n", dfs(1, n));
    print(1, n);
    return 0;
}

 

posted @ 2018-11-06 21:30  Sugewud  阅读(278)  评论(0编辑  收藏  举报