HLOJ436 添加括号

题面

题目描述
给定一个正整数序列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个中间和,按照从里到外,从左到右的顺序输出。

题解

这道题目和合并石子真的很像!每次合并的代价变成某一个区间的累加和。
所以只要处理一个前缀和数组就好了。

f[i][j]=max{f[i][k]+f[k+1][j]}+A[j]A[i1] f [ i ] [ j ] = m a x { f [ i ] [ k ] + f [ k + 1 ] [ j ] } + A [ j ] − A [ i − 1 ]

我们处理一个新数组 add[i][j]=k a d d [ i ] [ j ] = k ,表示 f[i][j] f [ i ] [ j ] 是由 f[i][k] f [ i ] [ k ] f[k+1][j] f [ k + 1 ] [ j ] 这两个状态合并而来。
那么输出方案递归求解。具体方法见代码

code

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int num=0;
    char c=' ';
    bool flag=true;
    for(;c>'9'||c<'0';c=getchar())
    if(c=='-')
    flag=false;
    for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
    return flag ? num : -num;
}
const int maxn=25;
int f[maxn][maxn],add[maxn][maxn];
int a[maxn],A[maxn];
int n;
void init()
{
    n=read();
    for(int i=1;i<=n;++i)
    {
        a[i]=read();
        A[i]=A[i-1]+a[i];
    }
    memset(f,10,sizeof f);
    for(int i=0;i<=n;i++)
        f[i][i]=0;
}
void DP()
{
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i+len-1<=n;i++)
        {
            int j=i+len-1;
            for(int k=i;k<j;k++)
                if(f[i][k]+f[k+1][j]+A[j]-A[i-1]<=f[i][j])
                {
                    f[i][j]=f[i][k]+f[k+1][j]+A[j]-A[i-1];//
                    //状态转移方程 
                    add[i][j]=k;
                    //合并的中间点 
                }
        }
    }
}//裸区间DP 
void work1(int l,int r)//递归输出方案 
{
    if(l==r)
    {
        printf("%d",a[l]);
        return ;
    }//如果区间被分解只剩下一个元素,那么直接输出, 
    printf("(");
    work1(l,add[l][r]);//中间点左边是加号左边 
    printf("+");
    work1(add[l][r]+1,r);//中间点右边是加号右边 
    printf(")");
} 
void work2(int l,int r)//输出区间和之和 
{
    if(!add[l][r])
        return ;
    work2(l,add[l][r]);
    work2(add[l][r]+1,r);
    if(l==1&&r==n)
    printf("%d",A[r]-A[l-1]);
    //输出格式,不忽略行末空格。 
    //所以这里要特判去掉空格。
    //坑爹的HLOJ要判WA的…… 
    else
    printf("%d ",A[r]-A[l-1]);
}
void print()
{
    work1(1,n);
    printf("\n%d\n",f[1][n]);
    work2(1,n);
}
int main()
{
    init();
    DP();
    print();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值