加分二叉树【题目连接】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-