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; }