我横竖睡不着,仔细看了半夜,才从字缝里看出字来,满本都写着四个字是‘区间DP’!

为什么是区间DP呢?因为是中序遍历啊!中序遍历有一个很大的特点就是,根在中间,二叉树的左右子树节点在根的左右对应。

那么我们只需要枚举根就好了,枚举根是不是就很像区间DP。对于一个区间,我们枚举分割点就相当于枚举根,然后一步步转移就好了,同时也要记录下每个区间的根是什么,也就是最优值是多少。

既然是DP,那也写一个转移方程吧(感觉题目已经给了诶)
图片说明

因为dfs其实更好写,所以用dfs更方便。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
#include<stack>
#define fs first
#define se second
#define pb push_back
#define cppio ios::sync_with_stdio(false);cin.tie(0)
#define eps 1e-7 
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> VI;

const int maxn=1e2+6;
const ll inf=0x3f3f3f3f;
const ll mod=1e9+7;

ll a[maxn];
int n;
ll dp[maxn][maxn];
ll root[maxn][maxn];

ll dfs(int l,int r){
    if(dp[l][r]!=-1) return dp[l][r];
    if(l>r) return 1;
    if(l==r){
        root[l][r]=l;
        return a[l];
    } 
    dp[l][r]=0;
    for(int i=l;i<=r;i++){
        ll tmp=dfs(l,i-1)*dfs(i+1,r)+a[i];
        if(tmp>dp[l][r]){
            root[l][r]=i;
            dp[l][r]=tmp;
        }
    }
    return dp[l][r];
}

void dfs2(int l,int r){
    if(l>r) return ;
    printf("%lld ",root[l][r]);
    dfs2(l,root[l][r]-1);
    dfs2(root[l][r]+1,r);
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",a+i);
    }
    memset(dp,-1,sizeof(dp));
    printf("%lld\n",dfs(1,n));
    dfs2(1,n);
    return 0;
}