1. 程式人生 > >P1040 加分二叉樹(樹上記憶化搜素)

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