如果整棵树的权值最大,必然有左子树的权值最大,右子树的权值也最大,符合最优性原理。实际上是区间动规:直接定义一个f[i][j]表示从i到j的最大值,则f[i][j]=max(f[i][k-1]*f[k+1][j]+a[k]),枚举k即可。要构造这个树,只需记录每次的决策值,令root(i,j)=k,表示中序遍历为i,i+1,…,j的二叉树的取最优决策时的根结点为k。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
addscoretree a=new addscoretree();
}
}
class addscoretree
{
private long[][] dp;
private int[][] root;
private int[] s;
private ArrayList<Integer> arrayList;
public addscoretree()
{
Scanner cin=new Scanner(System.in);
int n=cin.nextInt();
s=new int[n+1];
for(int i=1;i<=n;i++)
s[i]=cin.nextInt();
dp=new long[n+1][n+1];
root=new int[n+1][n+1];
arrayList=new ArrayList<>();
for(int i=1;i<=n;i++)
Arrays.fill(dp[i], -1);
for(int i=1;i<=n;i++)
{
dp[i][i]=s[i];
root[i][i]=i;
}
System.out.println(dp(1, n));
visit(1, n);
for(int i=0;i<arrayList.size();i++)
System.out.print(arrayList.get(i)+(i==arrayList.size()-1?"":" "));
// TODO Auto-generated constructor stub
}
private long dp(int i,int j)
{
if(i>j)
return 1;
if(dp[i][j]>-1)
return dp[i][j];
long max=Integer.MIN_VALUE;
for(int k=i;k<=j;k++)
{
long t1=dp(i, k-1);
long t2=dp(k+1, j);
if(t1*t2+s[k]>max)
{
max=t1*t2+s[k];
root[i][j]=k;
}
}
return dp[i][j]=max;
}
private void visit(int i,int j)
{
if(i>j)
return ;
if(i==j)
{
arrayList.add(i);
return ;
}
int k=root[i][j];
arrayList.add(k);
visit(i, k-1);
visit(k+1, j);
}
}
数组版本
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
addscoretree1 a=new addscoretree1();
}
}
class addscoretree1
{
private long[][] dp;
private int[][] root;
private int[] s;
private ArrayList<Integer> arrayList;
public addscoretree1()
{
Scanner cin=new Scanner(System.in);
int n=cin.nextInt();
s=new int[n+1];
for(int i=1;i<=n;i++)
s[i]=cin.nextInt();
dp=new long[n+2][n+2];
root=new int[n+2][n+2];
arrayList=new ArrayList<>();
for(int i=1;i<=n+1;i++)
Arrays.fill(dp[i], 1);
for(int i=1;i<=n;i++)
{
dp[i][i]=s[i];
root[i][i]=i;
}
for(int len=1;len<=n-1;len++) //区间长度
{
for(int i=1;i<=n;i++) //起点
{
int j=i+len; //终点
if(j<=n)
{
long temp=Integer.MIN_VALUE;
for(int k=i;k<=j;k++) //枚举根位置
{
if((dp[i][k-1]*dp[k+1][j]+s[k])>temp)
{
temp=dp[i][k-1]*dp[k+1][j]+s[k];
root[i][j]=k;
}
}
dp[i][j]=temp;
}
}
}
System.out.println(dp[1][n]);
visit(1, n);
for(int i=0;i<arrayList.size();i++)
System.out.print(arrayList.get(i)+(i==arrayList.size()-1?"":" "));
// TODO Auto-generated constructor stub
}
private void visit(int i,int j)
{
if(i>j)
return ;
if(i==j)
{
arrayList.add(i);
return ;
}
int k=root[i][j];
arrayList.add(k);
visit(i, k-1);
visit(k+1, j);
}
}