图专项——树形DP

题目https://www.luogu.org/problemnew/show/P1040

这是一个典型的树形DP
r o o t [ i ] [ j ] root[i][j] root[i][j]表示区间 i i i j j j构成二叉树根的位置
s o r c e [ i ] [ j ] sorce[i][j] sorce[i][j]表示区间 i i i j j j构成二叉树能取得的最大得分
d [ i ] d[i] d[i]表示第 i i i点的分数

状态转移方程为:
s o r c e [ i ] [ j ] = m a x ( s o r c e [ i ] [ j ] , s o r c e [ i ] [ k − 1 ] ∗ s o r c e [ k + 1 ] [ j ] + s o r c e [ k ] [ k ] ) sorce[i][j] = max(sorce[i][j],sorce[i][k-1]*sorce[k+1][j]+sorce[k][k]) sorce[i][j]=max(sorce[i][j],sorce[i][k1]sorce[k+1][j]+sorce[k][k])

其中要注意两个点:

  • s o r c e [ i ] [ i − 1 ] = 1 sorce[i][i-1] = 1 sorce[i][i1]=1 因为这样说明没有子树,根据题目要求设置为1
  • 在遍历起点 i i i 的时候需要后向前遍历,因为在求 s o r c e [ i ] [ k − 1 ] ∗ s o r c e [ k + 1 ] [ j ] + s o r c e [ k ] [ k ] sorce[i][k-1]*sorce[k+1][j]+sorce[k][k] sorce[i][k1]sorce[k+1][j]+sorce[k][k] 需要用到后边的数值,所以要先将后方的数值求出。
import java.util.Scanner;

public class Main{
    private int[][] root;
    private int[][] sorce;
    private int[] d;
    public static int size;

    public void read_data(){
        Scanner in = new Scanner(System.in);
        size = in.nextInt();
        root = new int[size+2][size+2];
        sorce = new int[size+2][size+2];
        d = new int[size+2];

        for(int i=1;i<=size;i++){
            d[i] = in.nextInt();
            sorce[i][i] = d[i];
            sorce[i][i-1] = 1;
        }
        sorce[size+1][size] = 1;
        in.close();
    }

    public void dp(){

        for(int i=size;i>=1;i--){
            for(int j=i+1;j<=size;j++){
                for(int k=i;k<=j;k++){
                    if(sorce[i][k-1]*sorce[k+1][j]+sorce[k][k]>sorce[i][j]){
                        sorce[i][j] = sorce[i][k-1]*sorce[k+1][j]+sorce[k][k];
                        root[i][j] = k;
                    }
                }
            }
        }
        System.out.println(sorce[1][size]);
    }

    public void output(int l,int r){
        if(l>r) return;
        if(l == r){
            System.out.printf("%d ",r);
            return;
        }

        System.out.printf("%d ",root[l][r]);
        output(l,root[l][r]-1);
        output(root[l][r]+1,r);

    }

    public static void main(String arg[]){
        Main obj = new Main();
        obj.read_data();
        obj.dp();
        obj.output(1,obj.size);
    }
}

也可以利用记忆化搜索。

你可能感兴趣的:(ACM——cpp)