Vijos 1100 (区间DP)

题目链接https://vijos.org/p/1100

题目大意:NOIP著名的加分二叉树。给出一棵树的中序遍历,加分规则左子树*右子树+根。空子树分数为1。问最大加分的树结构,输出树结构的先序遍历。

解题思路

先从小的问题看起。

对于一棵子树,只要知道根是啥,就能轻松求出这棵子树的加分情况。

那么就变成枚举根的区间DP问题。

由于要输出先序遍历,则用m[i][j]记录在i~j区间选择的根。

区间DP边界:

①一个点情况:即无左右子树,dp[i][i]=node[i],m[i][i]=i.

②两个点情况,即无右子树。dp[i][i+1]=node[i]+node[i+1];m[i][i+1]=i。

注意为什么DP边界是两种情况,是因为区间DP枚举中间分割点时,是按照常规处理左区间和右区间的,以上两种情况,左右区间都是有问题的。

所以需要特别预处理。

区间DP:

推荐先枚举区间间隔p的写法,这里直接从p=2开始计算。p=0,p=1已经预处理。

对于dp[i][j],则根k的范围(i+1,j),按照规则写方程就行了。m[i][j]=k,记录每个区间的根。

则最后ans=dp[1][n]。

递归打印方案,先序遍历是根左右,不要打错了。

#include "cstring"
#include "cstdio"
#include "cstring"
int dp[31][31],m[31][31],node[31];
void print(int i,int j)
{
    printf("%d ",m[i][j]);
    if(i<=m[i][j]-1) print(i,m[i][j]-1);
    if(m[i][j]+1<=j) print(m[i][j]+1,j);
}
int main()
{
    //freopen("in.txt","r",stdin);
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        for(int i=1; i<=n; i++) scanf("%d",&node[i]);
        for(int i=1; i<=n; i++)
        {
            dp[i][i]=node[i];
            m[i][i]=i;
            dp[i][i+1]=node[i]+node[i+1];
            m[i][i+1]=i;
        }
        for(int p=2; p<=n; p++)
        {
            for(int i=1; i<=n; i++)
            {
                int j=i+p;
                if(j>n) break;
                dp[i][j]=1;
                for(int k=i+1; k<j; k++)
                {
                    if((dp[i][k-1]*dp[k+1][j]+node[k])>dp[i][j])
                    {
                        dp[i][j]=dp[i][k-1]*dp[k+1][j]+node[k];
                        m[i][j]=k;
                    }
                }
            }
        }
        printf("%d\n",dp[1][n]);
        print(1,n);
    }
}
时间: 2024-10-25 19:13:57

Vijos 1100 (区间DP)的相关文章

Vijos 1565 多边形 【区间DP】

描述 zgx给了你一个n边的多边形,这个多边形每个顶点赋予一个值,每条边都被标上运算符号+或*,对于这个多边形有一个游戏,游戏的步骤如下:(1)第一步,删掉一条边:(2)接下来n-1步,每步对剩下的边中的一条进行操作,用一个新的顶点取代这条边.将这条被取代的边两端的顶点的整数值通过边上的运算得到的结果赋予新顶点. 最后,所有的边被删除,只剩一个定点,这个定点的整数值就是游戏的最后得分. 你要做的就是算出给你的多边形能得到的最高分和最低分. 格式 输入格式 第一行,n;第二行,n条边的运算符:第三

Vijos 1451 圆环取数 【区间DP】

背景 小K攒足了路费来到了教主所在的宫殿门前,但是当小K要进去的时候,却发现了要与教主守护者进行一个特殊的游戏,只有取到了最大值才能进去Orz教主-- 描述 守护者拿出被划分为n个格子的一个圆环,每个格子上都有一个正整数,并且定义两个格子的距离为两个格子之间的格子数的最小值.环的圆心处固定了一个指针,一开始指向了圆环上的某一个格子,你可以取下指针所指的那个格子里的数以及与这个格子距离不大于k的格子的数,取一个数的代价即这个数的值.指针是可以转动的,每次转动可以将指针由一个格子转向其相邻的格子,且

POJ 2955 Brackets (区间DP)

题意:给定一个序列,问你最多有多少个合法的括号. 析:区间DP,dp[i][j] 表示在 第 i 到 第 j 区间内最多有多少个合法的括号. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cstdlib> #include <cmath> #include <ios

poj 3280(区间DP)

Cheapest Palindrome Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7869   Accepted: 3816 Description Keeping track of all the cows can be a tricky task so Farmer John has installed a system to automate it. He has installed on each cow a

【codevs2211】WJM2BMR安慰MM记 区间dp

事实上这道题的原型是1258——关路灯,为什么选了这个呢? 废话,因为题目呗= = 言规正传,觉得这道题扯到看不懂的可以去翻codevs1258,说的挺易懂的 其实就是一道区间dp,比较裸的代码,考虑清楚就好 记录过去和回来的最小安慰时间= = 贴代码 var n,c,i,j,sum:longint; m,w:array[0..1100] of longint; a,l,r:array[0..1005,0..1005] of longint; function min(a,b:longint):

[NYIST15]括号匹配(二)(区间dp)

题目链接:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=15 经典区间dp,首先枚举区间的大小和该区间的左边界,这时右边界也可计算出来.首先初始化一个匹配,那就是看看这两个括号是否匹配,即: (s[i] == '(' && s[j] == ')') || (s[i] == '[' && s[j] == ']') ? dp(i,j) = dp(i+1,j-1)+2) : dp(i,j) = 0 接下来枚举i和j中间的所

HDU 4745 Two Rabbits (区间DP)

题意:给定一个圆形的环,有两个只兔子,一只顺时针跳,一个逆时针,但每次跳到的石头必须一样,问你最多能跳多少轮. 析:本来以为是LCS呢,把那个序列看成一个回文,然后就能做了,但是时间受不了.其实是一个区间DP,dp[i[j] 表示从 i 到 j 中最长的回文数. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #in

&lt;区间DP&gt; 乘法游戏

链接:http://nanti.jisuanke.com/t/213 乘法游戏是在一行牌上进行的.每一张牌包括了一个正整数.在每一个移动中,玩家拿出一张牌,得分是用它的数字乘以它左边和右边的数,所以不允许拿第1张和最后1张牌.最后一次移动后,这里只剩下两张牌. 你的目标是使得分的和最小. 例如,如果数是10  1  50  20  5,依次拿1.20.50,总分是10*1*50+50*20*5+10*50*5=8000 而拿50.20.1,总分是1*50*20+1*20*5+10*1*5=115

POJ 3280 Cheapest Palindrome ( 区间DP &amp;&amp; 经典模型 )

题意 : 给出一个由 n 中字母组成的长度为 m 的串,给出 n 种字母添加和删除花费的代价,求让给出的串变成回文串的代价. 分析 :  原始模型 ==> 题意和本题差不多,有添和删但是并无代价之分,要求最少操作几次才能是其变成回文串 ① 其实细想之后就会发现如果没有代价之分的话删除和增添其实是一样的,那么除了原串原本拥有的最长回文串不用进行考虑处理,其他都需要进行删除或者增添来使得原串变成回文串,所以只要对原串 S 和其反向串 S' 找出两者的最长公共子串长度 L 再用总长减去 L 即可 ②