加分二叉樹【題目連接】ios
感受我超廢數組
這道題當時壓根就不會qwq(我卻是挺適合寫rand的qwq)優化
對於暴力的作法:spa
優化的效果:優化前:優化後:效果仍是很明顯的;code
4.而後枚舉每一個點作這棵子樹的根(for循環)這裏爲了一會輸出前序遍歷,因此要開數組root[i][j]記錄每一段的根是什麼;blog
以上就是暴力DFS的思路,如下是代碼:遞歸
#include<iostream> #include<cstdio> using namespace std; int n,v[31],mem[31][31],root[31][31]; int dfs(int l,int r){ if(mem[l][r]>0)return mem[l][r];//當這個點已經被計算過期,直接返回 if(l==r)return v[l];//當只有一個點時,它的值就爲自己 if(r<l)return 1;//當出現左右顛倒的狀況,也就是空子樹,加分爲1 for(int i=l;i<=r;i++){//分別枚舉每一個點作子樹的根 int p=dfs(l,i-1)*dfs(i+1,r)/*左子樹的值×右子樹的值*/+mem[i][i]/*+根結點的值*/; if(p>mem[l][r]){ mem[l][r]=p;/*求最大值*/root[l][r]=i/*記錄路徑用來前序遍歷*/; } } return mem[l][r]; } void print(int l,int r){//前序遍歷 if(r<l)return; if(l==r){printf("%d ",l);return;} printf("%d ",root[l][r]); print(l,root[l][r]-1); print(root[l][r]+1,r); } int main(){ freopen("binary.in","r",stdin); freopen("binary.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&v[i]),mem[i][i]=v[i]; printf("%d\n",dfs(1,n)); print(1,n); return 0; }
對於被稱之爲正解的作法:ci
區間dp的作法:get
第一層for循環l:枚舉以幾個點建子樹,能夠是2~n;io
第二層for循環i:枚舉一個區間,需知足i+l<=n+1(舉個例子:n=5,以兩個點建子樹時,i的取值分別爲1,2,3,4,相應的區間爲[1,2],[2,3],[3,4],[4,5])
第三層for循環k:枚舉樓上劃分出的區間內以哪一個點爲根,計算加分,記錄其中的最大值(小注意:還要開一個數組用來前序遍歷,遍歷和dfs的思想是同樣的,記錄的話也同樣,這裏用w數組);
樣例:
#include <iostream> using namespace std; int n; int d[31]; long long dp[31][32]; //dp[i][j] -> answer in [i,j) int w[31][32]; void dfs(int l, int r) { cout << w[l][r] << ' '; if (w[l][r] > l) dfs(l, w[l][r]); if (w[l][r] + 1 < r) dfs(w[l][r] + 1, r); } int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> dp[i][i + 1], dp[i][i] = 1, w[i][i + 1] = i; for (int l = 2; l <= n; l++) { for (int i = 1; i + l <= n + 1; i++) { int j = i + l; for (int k = i; k < j; k++) { if (dp[i][k] * dp[k + 1][j] + dp[k][k + 1] > dp[i][j]) { dp[i][j] = dp[i][k] * dp[k + 1][j] + dp[k][k + 1]; w[i][j] = k; } } } } cout << dp[1][n + 1] << endl; dfs(1, n + 1); }
end-