import java.util.*;

/**
 * NC333 加分二叉树
 * @author d3y1
 */
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param scores int整型ArrayList
     * @return int整型ArrayList<ArrayList<>>
     */
    public ArrayList<ArrayList<Integer>> scoreTree (ArrayList<Integer> scores) {
        return solution1(scores);
        // return solution2(scores);
    }

    /**
     * 动态规划 + 滑动窗口
     *
     * dp[i][j]表示节点区间[i,j]所能获得的最高加分
     * root[i][j]表示节点区间[i,j]获得最高加分时的根节点编号
     * 
     *            { scores.get(i-1)                       , i=j
     * dp[i][j] = {     { dp[k][k]+dp[k+1][j]             , i<j && k=i
     *            { max { dp[k][k]+dp[i][k-1]             , i<j && k=j
     *            {     { dp[k][k]+dp[i][k-1]*dp[k+1][j]  , i<j && i<k<j
     *
     * @param scores
     * @return
     */
    private ArrayList<ArrayList<Integer>> solution1(ArrayList<Integer> scores){
        int n = scores.size();

        int[][] root = new int[n+1][n+1];
        int[][] dp = new int[n+1][n+1];

        int score;
        // 滑动窗口
        for(int gap=0; gap<n; gap++){
            for(int i=1,j=i+gap; j<=n; i++,j++){
                if(gap == 0){
                    dp[i][j] = scores.get(i-1);
                    root[i][j] = i;
                }else{
                    for(int k=i; k<=j; k++){
                        if(k == i){
                            score = dp[k][k]+dp[k+1][j];
                            if(score > dp[i][j]){
                                dp[i][j] = score;
                                root[i][j] = k;
                            }
                        }else if(k == j){
                            score = dp[k][k]+dp[i][k-1];
                            if(score > dp[i][j]){
                                dp[i][j] = score;
                                root[i][j] = k;
                            }
                        }else{
                            score = dp[k][k]+dp[i][k-1]*dp[k+1][j];
                            if(score > dp[i][j]){
                                dp[i][j] = score;
                                root[i][j] = k;
                            }
                        }
                    }
                }
            }
        }

        ArrayList<ArrayList<Integer>> result = new ArrayList<>();
        ArrayList<Integer> maxScore = new ArrayList<>();
        ArrayList<Integer> seq;

        maxScore.add(dp[1][n]);
        result.add(maxScore);

        seq = preOrder(root, 1, n, new ArrayList<>());
        result.add(seq);

        return result;
    }

    /**
     * 前序遍历
     * @param range
     * @param left
     * @param right
     * @param seq
     * @return
     */
    private ArrayList<Integer> preOrder(int[][] range, int left, int right, ArrayList<Integer> seq){
        if(left > right){
            return new ArrayList<>();
        }
        int root = range[left][right];
        seq.add(root);
        preOrder(range, left, root-1, seq);
        preOrder(range, root+1, right, seq);

        return seq;
    }

    /**
     * 动态规划 + 滑动窗口
     * 
     * dp[i][j]表示节点区间[i,j]所能获得的最高加分
     * 
     * map.put(key, value) 
     *   key: i+","+j -> 表示节点区间[i,j]
     * value: seq     -> 表示节点区间[i,j]获得最高加分时的前序遍历顺序
     *
     *            { scores.get(i-1)                       , i=j
     * dp[i][j] = {     { dp[k][k]+dp[k+1][j]             , i<j && k=i
     *            { max { dp[k][k]+dp[i][k-1]             , i<j && k=j
     *            {     { dp[k][k]+dp[i][k-1]*dp[k+1][j]  , i<j && i<k<j
     * 
     * @param scores
     * @return
     */
    private ArrayList<ArrayList<Integer>> solution2(ArrayList<Integer> scores){
        int n = scores.size();

        HashMap<String, ArrayList<Integer>> map = new HashMap<>();
        int[][] dp = new int[n+1][n+1];

        int score;
        String key;
        ArrayList<Integer> seq;
        // 滑动窗口
        for(int gap=0; gap<n; gap++){
            for(int i=1,j=i+gap; j<=n; i++,j++){
                key = i+","+j;
                if(gap == 0){
                    dp[i][j] = scores.get(i-1);
                    seq = new ArrayList<>();
                    seq.add(i);
                    map.put(key, seq);
                }else{
                    for(int k=i; k<=j; k++){
                        if(k == i){
                            score = dp[k][k]+dp[k+1][j];
                            if(score > dp[i][j]){
                                dp[i][j] = score;
                                seq = new ArrayList<>();
                                seq.addAll(map.get(k+","+k));
                                seq.addAll(map.get((k+1)+","+j));
                                map.put(key, seq);
                            }
                        }else if(k == j){
                            score = dp[k][k]+dp[i][k-1];
                            if(score > dp[i][j]){
                                dp[i][j] = score;
                                seq = new ArrayList<>();
                                seq.addAll(map.get(k+","+k));
                                seq.addAll(map.get(i+","+(k-1)));
                                map.put(key, seq);
                            }
                        }else{
                            score = dp[k][k]+dp[i][k-1]*dp[k+1][j];
                            if(score > dp[i][j]){
                                dp[i][j] = score;
                                seq = new ArrayList<>();
                                seq.addAll(map.get(k+","+k));
                                seq.addAll(map.get(i+","+(k-1)));
                                seq.addAll(map.get((k+1)+","+j));
                                map.put(key, seq);
                            }
                        }
                    }
                }
            }
        }

        ArrayList<ArrayList<Integer>> result = new ArrayList<>();
        ArrayList<Integer> maxScore = new ArrayList<>();
        maxScore.add(dp[1][n]);
        result.add(maxScore);
        result.add(map.get(1+","+n));

        return result;
    }
}