小美有一个由N个点组成的二叉树,每个点有一个权值。定义二叉树每个点的开销为其权值与深度的乘积(每个点的深度为其到根节点的距离,即其到根节点的路径上的边数),二叉树的总开销即每个点的开销之和。小美按照二叉树的中序遍历依次记录下每个点的权值,即她记录下了N个数,第i个数表示位于中序遍历第i个位置的点的权值。之后由于某种原因,小美遗忘了二叉树的具体结构。在所有可能的二叉树中,总开销最小的二叉树被称为最优二叉树。现在,小美请小团求出最优二叉树的总开销。
小美有一个由N个点组成的二叉树,每个点有一个权值。定义二叉树每个点的开销为其权值与深度的乘积(每个点的深度为其到根节点的距离,即其到根节点的路径上的边数),二叉树的总开销即每个点的开销之和。小美按照二叉树的中序遍历依次记录下每个点的权值,即她记录下了N个数,第i个数表示位于中序遍历第i个位置的点的权值。之后由于某种原因,小美遗忘了二叉树的具体结构。在所有可能的二叉树中,总开销最小的二叉树被称为最优二叉树。现在,小美请小团求出最优二叉树的总开销。
第一行输入一个整数N(1<=N<=300),表示二叉树的点数。
第二行输入N个由空格隔开的整数,表示按中序遍历记录下的各个点的权值,所有权值均为不超过10^4的正整数。
输出一个整数,表示最优二叉树的总开销。
5 7 6 5 1 3
21
最优二叉树如图所示,总开销为7+5+3*2+1*3=21。
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.util.Arrays; public class Main { static int[][] mem; static int[] weight; public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine().trim()); mem = new int[n][n]; for(int i = 0; i < n; i++) Arrays.fill(mem[i], -1); String[] strW = br.readLine().trim().split(" "); br.close(); weight = new int[n]; for(int i = 0; i < n; i++) weight[i] = Integer.parseInt(strW[i]); // 从第0层开始 System.out.println(recur(0, n - 1)); } // 计算用[left,right]的元素构建子树的最小开销 private static int recur(int left, int right) { // 无法构成子树,返回0开销 if(left >= right) return 0; // 该子树的最小开销已经计算过,直接返回 if(mem[left][right] != -1) return mem[left][right]; int rawWeightSum = 0; // 首先,每个节点都要加上其本身的权重 for(int i = left; i <= right; i++) rawWeightSum += weight[i]; int cost = Integer.MAX_VALUE; // 穷举根节点,继续向下构建子树,每向下构建一次子树,就会把子树节点的权重再加一次,相当于考虑了深度 for(int i = left; i <= right; i++){ // 左子树权值+右子树权值+全部节点权值-根节点权值 cost = Math.min(cost, recur(left, i - 1) + recur(i + 1, right) + rawWeightSum - weight[i]); } mem[left][right] = cost; return cost; } }
#include<bits/stdc++.h> #include <iostream> using namespace std; int main() { int n; cin>>n; vector<int>t(301,0); vector<vector<int>>dp(301,vector<int>(301,0)); for(int i=0;i<n;i++) cin>> t[i]; for(int j=0;j<=n-1;j++) { for(int i=0;i+j<n;i++) //[i,i+j] { int all=0; for(int k=i;k<=i+j;k++) all += t[k]; int minc=INT_MAX; for(int k=i;k<=i+j;k++) { minc=min(minc,dp[i][k-1]+dp[k+1][i+j]+all-t[k]); } dp[i][i+j]=minc; } } cout<<dp[0][n-1]<<'\n'; return 0; }
from collections import deque n=int(input()) w=list(map(int, input().strip().split())) class tree: def __init__(self, val=0, left=None, right=None): self.left=left self.right=right self.val=val def process(nums): if not nums: return num=max(nums) idx=nums.index(num) root=tree(num) root.left=process(nums[:idx]) root.right=process(nums[idx+1:]) return root def pro(root): if not root: return 0 res=root.val res+=pro(root.left)+pro(root.right) return res min_res=float('inf') extra=sum(w) for i in range(len(w)): root=tree(w[i]) root.left=process(w[:i]) root.right=process(w[i+1:]) que=deque([root]) res=0 while que: node=que.popleft() res+=pro(node) if node.left: que.append(node.left) if node.right: que.append(node.right) min_res=min(min_res, res-extra) print(min_res)
//C++ dp树实现,思路细节请看楼上大佬的Java注解。 #include <iostream> #include <vector> #include <string.h> using namespace std; int ve[310]; int dp[310][310]; int f(int l, int r, int level) { if (l > r) { return 0; } if (dp[l][r]!=-1) { return dp[l][r]; } int ret = 2e9, all = 0; for (int i = l; i <= r; i++) { all += ve[i]; } for (int i = l; i <= r; i++) { int left = f(l, i - 1, level+1); int right = f(i + 1, r, level+1); ret = min(ret, left + right + all - ve[i]); } dp[l][r] = ret; return ret; } int main() { int n; cin >> n; for (int i = 0; i < n; i++) { cin >> ve[i]; } memset(dp, -1, sizeof dp); f(0, n - 1, 0); cout << dp[0][n-1] << endl; return 0; }
#include <bits/stdc++.h> using namespace std; int n, a[305], dp[305][305]; int pre_a[305]; int main() { scanf("%d", &n); for (int i = 0; i < n; i++) { scanf("%d", &a[i]); } pre_a[0] = 0; for (int i = 1; i <= n; i++) { pre_a[i] = pre_a[i - 1] + a[i - 1]; } memset(dp, 0x3f3f3f3f, sizeof dp); for (int i = 0; i < n; i++) { dp[i][i] = a[i]; dp[i][i - 1] = 0; } for (int i = n - 1; i >= 0; i--) { for (int j = i + 1; j < n; j++) { for (int k = i; k <= j; k++) { int left = 0, right = 0; if (k != i) { left = dp[i][k - 1] + pre_a[k] - pre_a[i]; } if (k != j) { right = dp[k + 1][j] + pre_a[j + 1] - pre_a[k + 1]; } dp[i][j] = min(dp[i][j], left + right + a[k]); } } } printf("%d\n", dp[0][n - 1] - pre_a[n]); return 0; }