zoukankan      html  css  js  c++  java
  • 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
    
    
    
     
  • 相关阅读:
    Linux kernel AACRAID Driver Compat IOCTL 本地安全绕过漏洞
    WordPress Contact Form 7插件任意文件上传漏洞
    文件上传
    Po类设计
    MySQL 表设计
    Mybatis——更新DB表的字段时,应该注意的点
    分页——为Mybatis配置PageHelper
    SpringAOP配置与使用(示例)
    日志问题
    为一台全新的电脑构建JavaEE开发环境
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/9492812.html
Copyright © 2011-2022 走看看