题目链接:https://www.luogu.com.cn/problem/P1040
思路一:记忆化搜索
要寻找区间之间的关系,寻找谁是谁的子树,谁是根节点。
dp(l,r)表示区间l,r区间所能凑出的最大值是多少,然后不停的递归下去,就能找到所有的根节点,在确定最大值的
同时求出这棵树。
(可以考虑逆向的做法,因为通常都是给出树让你进行遍历或者求树上的最值,但是如果树不确定,我们可以在搜索最大值的时候建树就好了)。
代码一:
#include <bits/stdc++.h>
using namespace std;
const int N = 220;
typedef long long ll;
ll dp[N][N] = {0};
int n,tot = 0,b[N],path[N][N] = {0};
ll dfs(int l,int r)
{
if(l>r) return 1;
if(dp[l][r] > 0) return dp[l][r];
for(int i=l;i<=r;i++){
ll tp = dfs(l,i-1)*dfs(i+1,r) + dp[i][i];
if(tp > dp[l][r]){ //求出最大值
dp[l][r] = tp; path[l][r] = i; //路径记录
}
}
return dp[l][r];
}
void fd(int l,int r)
{
if(l>r) return ;
if(l == r) { b[++tot] = l; return; }
b[++tot] = path[l][r];
fd(l,path[l][r]-1);
fd(path[l][r]+1,r);
}
int main(void)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&dp[i][i]);
printf("%lld\n",dfs(1,n));
fd(1,n);
for(int i=1;i<=tot;i++)
{
if(i>1) printf(" ");printf("%d",b[i]);
}
return 0;
}
思路一:
就是区间dp,注意一定要判断没有左(右)子树的情况。
代码二:
#include <bits/stdc++.h>
using namespace std;
const int N = 220;
typedef long long ll;
ll dp[N][N] = {0};
int path[N][N] = {0},n,b[N],tot = 0;
void dfs(int l,int r)
{
if(l > r) return ;
if(l == r) { b[++tot] = l; return ; }
b[++tot] = path[l][r];
dfs(l,path[l][r] - 1);
dfs(path[l][r] + 1,r);
}
int main(void)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&dp[i][i]),dp[i][i-1] = 1ll,path[i][i] = i;
for(int i=2;i<=n;i++) //枚举区间长度
{
for(int l=1;l+i-1<=n;l++) //枚举左区间
{
int r = l+i-1;
dp[l][r] = dp[l][l] + dp[l+1][r]; //注意,这里要初始化,因为可能存在没有左子树的情况。
path[l][r] = l;
for(int j=l+1;j<=r;j++) //可以求出只有右子树的情况
{
if(dp[l][j-1]*dp[j+1][r] + dp[j][j] > dp[l][r])
{
dp[l][r] = dp[l][j-1]*dp[j+1][r] + dp[j][j];
path[l][r] = j;
}
}
}
}
dfs(1,n);
printf("%lld\n",dp[1][n]);
for(int i=1;i<=n;i++)
{
if(i>1) printf(" ");
printf("%d",b[i]);
}
return 0;
}