P2308 添加括号(区间DP)

题目背景

给定一个正整数序列a(1),a(2),...,a(n),(1<=n<=20)

不改变序列中每个元素在序列中的位置,把它们相加,并用括号记每次加法所得的和,称为中间和。

例如:

给出序列是4,1,2,3。

第一种添括号方法:

((4+1)+(2+3))=((5)+(5))=(10)

有三个中间和是5,5,10,它们之和为:5+5+10=20

第二种添括号方法

(4+((1+2)+3))=(4+((3)+3))=(4+(6))=(10)

中间和是3,6,10,它们之和为19。

题目描述

现在要添上n-1对括号,加法运算依括号顺序进行,得到n-1个中间和,求出使中间和之和最小的添括号方法。

输入输出格式

输入格式:

 

共两行。 第一行,为整数n。(1< =n< =20) 第二行,为a(1),a(2),...,a(n)这n个正整数,每个数字不超过100。

 

输出格式:

 

输出3行。 第一行,为添加括号的方法。 第二行,为最终的中间和之和。 第三行,为n-1个中间和,按照从里到外,从左到右的顺序输出。

 

输入输出样例

输入样例#1: 
4
4 1 2 3
输出样例#1: 
(4+((1+2)+3))
19
3 6 10

题解:第二问的答案其实就是合并石头,dp(i,j)表示i到j 合并后的最小值。
首先需要用sum[i]预处理sum[j]-sum[i-1]表示区间的合并和;
dp(i,j) = min(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
k表示枚举的断点,i<=k<j;

重点就是第一问和第三问。

对于这两问可以一起求。

用一个 dfs 从[1,n] 这个状态往回推每次找到一个 x 表示 当前 [l,r] 状态是用 [l,x] 和 [x+1,r] 转移而来的。

然后对于第一问,可以这样考虑,记录一个 numL[i] 表示 i 作为 l 被递归了几次。由题目的观察可以发现,i 作为 l 被递归的次数(除 l=r=i的情况时) 就是 i 前面(与 i 相连)的左括号数。

                                          类似的,记录一个numR[i] 表示 i 作为 r 被递归了几次。还是由题目的观察可以发现,i 作为 r 被递归的次数(除 l=r=i的情况时) 就是 i 后面的(与 i 相连)右括号数。

当然 如果numL[i]≠0 则 numR[i]=0  因为除去了 l=r=i 的情况时。

然后在输出的时候 对于每一个数前面输出 numL[i] 个‘(’   后面输出numR[i] 个‘)’    然后在两个数之间加 ‘+’字符即可。

对于第二问,由于是dfs将[1,n]状态进行分割,所以就是在回溯时,直接记录 sum[l,r] 就好辣。

然后就愉快的AC了


#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
int kr[50],pa[50][50],a[50],kl[50],g[50][50],dp[50][50],sum[61];
void dfs(int x , int y)
{
    if(x==y)
    return ;
    kl[x]++;
    kr[y]++;
    dfs(x,g[x][y]);
    dfs(g[x][y]+1,y);
}
void DFS(int x , int y)
{
    if(x==y)
    return ;
    DFS(x,g[x][y]);
    DFS(g[x][y]+1,y);
    cout<<sum[y]-sum[x-1]<<' ';
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1 ; i<=n ; i++)
    {
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
        dp[i][i]=0;
    }
    for(int i=n-1 ; i ; i--)
    {
        for(int j=i+1 ; j<=n ; j++)
        {
            dp[i][j]=1e9;
            for(int k=i ; k<j ; k++)
            {
                if(dp[i][j]>=dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1])
                {
                    dp[i][j]=dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1];
                    g[i][j]=k;
                }
            }
        }
    }
    dfs(1,n);
    for(int i=1 ; i<=n ; i++)
    {
        for(int j=1 ; j<=kl[i] ; j++)
        cout<<'(';
        cout<<a[i];
        if(!kr[i]&&i!=n)
        cout<<'+';
        for(int j=1 ; j<=kr[i] ; j++)
        cout<<')';
        if(kr[i]&&i!=n)
        cout<<'+';
    }
    cout<<endl;
    cout<<dp[1][n]<<endl;
    DFS(1,n);
}
View Code

 

 
posted @ 2018-08-17 13:20  shuai_hui  阅读(290)  评论(0编辑  收藏  举报