您的位置:首页 > 其它

·树形动规(一) 加分二叉树(洛谷p1040)

2018-04-05 18:37 260 查看
 今天开始写树形动规
由于篇幅限制,不打出题目,以下是本题链接:(洛谷网校p1040) https://www.luogu.org/problemnew/show/P1040  
一、简介
树形动规是经处理后以树的逻辑结构解决的一类问题,而处理前的结构可以是多种多样。可以本身就是一棵树,也可以是一条链或一个强连通图等;但处理后变成一棵树。
常见解决策略是转为二叉树解题;多分叉的树可采取“左孩子-右兄弟”模式。然后以二维动规求解。一维表示树的节点,另一维则为节点与其分支的分配处理。
 
二、正题
本题虽为名义上的树形动规,但本质上是用区间动规解决的树形问题,因此动规的两维不采用”简介”中的说法。
1.最优子结构处理:
记f(i,j)为数组从i到j可获得的最大加分,我们就刻画了最优解的结构。而假定f(i,j)的根为k,则i...k-1,k+1...j应分别到最大值,否则就必有另一解可取代它成为最优解(想想为什么?)
2.状态转移方程:
根据一的指导思想,联系题意,易得:
f(i,j) = max{f(i,j),f(i,k-1)*f(k+1,j) + a(k)} i+1 < k < j-1
3.边界条件:
f(i,i)= a(i);   //只有根节点的加分二叉树
f(i,i+1)= a(i) + a(j)*1    //有一片叶子的加分二叉树
4.关于输出最优解的方法:
建立e二维数组g,初始化
     g(i,i)= i;  //表示根为自己
每当f(i,j)被k更新,g(i,j) = k.
最后深度优先搜索即可。
 
三、代码#include<cstdio>

int n;
int a[35];
long int f[35][35];
long int g[35][35];
int flag = false;

void dfs(int b,int p)
{
if(b > p)
return;
if(flag)
printf(" ");
else
flag = true;
printf("%d",g[p]+1);
dfs(b,g[b][p]-1);
dfs(g[b][p]+1,p);
}

int main()
{
scanf("%d",&n);
for(int i = 0;i < n;i++)
scanf("%d",a+i);
for(int i = 0;i < n;i++)
{
g[i][i] = i;
f[i][i] = a[i];
if(i+1 < n)
{
f[i][i+1] = a[i] + a[i+1];
g[i][i+1] = i;
}
}
for(int mov = 2;mov < n;mov++)
for(int i = 0,j = i+mov;j < n;i++,j++)
{
f[i][j] = f[i][i] + f[i+1][j];
g[i][j] = i;
for(int k = i+1;k < j;k++)
{
if(f[i][k-1] * f[k+1][j] + a[k] >f[i][j])
{
f[i][j] = f[i][k-1] * f[k+1][j]+ a[k];
g[i][j] = k;
}
}
if(f[i][j] < f[i][j-1] + a[j])
{
f[i][j] = f[i][j-1] + a[j];
g[i][j] = j;
}
}
printf("%d\n",f[0][n-1]);
dfs(0,n-1)
return 0;
}


[b]四、注意点提示

1.关于输出数的字典序
    一般输出字典序最小的一组解。做法是将循环顺序定为从字典序小端到大端,更新条件不包括等于。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息