zoukankan      html  css  js  c++  java
  • 添加括号 vijos1038 动态规划 区间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个中间和,求出使中间和之和最小的添括号方法。如果有多组最优解,考虑将括号尽可能往前放。

    样例输入

    4
    4 1 2 3

    样例输出

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

    这道题和合并石子那一道DP很像,不同的是这一次要打印路径。

    因为要记录路径,所以选择使用记忆化搜索,比DP的代码更清晰易读。

    用root[l,r]表示这个区间的断点位置,dp[i][j]表示[i,j]区间的最优答案

    显然有:

    0、对于一段区间合并产生的价值,就是这个区间的值的和。因此我们可以采用前缀和进行优化

    1、对于任意 i∈[1,n] 有 dp[i][i] = 0 (不用合并)

    2、对于任意 i∈[1,n) 有 dp[i][i+1]=a[i]+a[i+1] (只有两个,一定合并)

    3、对于任意一组 (l,r),r>l且r-l>1,那么就有dp[l,r]=min(dp[l][i]+dp[i+1][r]+sum[r]-sum[l-1];)其中i∈[l,r)  (枚举断点 区间DP)

    取到最优值的时候,记录断点即可。后续递归输出。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    template<class T> inline void read(T &_a){
        bool f=0;int _ch=getchar();_a=0;
        while(_ch<'0' || _ch>'9'){if(_ch=='-')f=1;_ch=getchar();}
        while(_ch>='0' && _ch<='9'){_a=(_a<<1)+(_a<<3)+_ch-'0';_ch=getchar();}
        if(f)_a=-_a;
    }
    
    int n,a[21],dp[21][21],root[21][21],sum[21],zuo[21],you[21];
    bool vis[21][21];
    
    void dfs(int l,int r)
    {
        vis[l][r]=true;
        if(l==r) {dp[l][r]=0; return ;}
        for (register int i=1;i<=n;++i)
            for (register int v=l;v+i-1<=r;++v)
                if(!vis[v][v+i-1]) dfs(v,v+i-1);
        if(r==l+1)
        {
            root[l][r]=l;
            dp[l][r]=a[l]+a[r];
            return ;
        }
        for (register int i=l;i<r;++i)
        {
            if(dp[l][i]+dp[i+1][r]+sum[r]-sum[l-1]<=dp[l][r])
            {
                dp[l][r]=dp[l][i]+dp[i+1][r]+sum[r]-sum[l-1];
                root[l][r]=i;
            }
        }
    }
    
    void print(int l,int r)
    {
        if(l==r) printf("%d",a[l]);
        if(l>=r) return ;
        printf("(");
        print(l,root[l][r]);
        printf("+");
        print(root[l][r]+1,r);
        printf(")");
    }
    
    void prints(int l,int r)
    {
        if(!root[l][r]) return ;
        prints(l,root[l][r]);
        prints(root[l][r]+1,r);
        printf("%d ",sum[r]-sum[l-1]);
    }
    
    int main()
    {
        read(n);
        for (register int i=1;i<=n;++i) read(a[i]),sum[i]=sum[i-1]+a[i];
        memset(dp,0x7f,sizeof(dp));
        dfs(1,n);
        print(1,n);
        printf("
    %d
    ",dp[1][n]);
        prints(1,n);
        return 0;
    }
  • 相关阅读:
    docker 常用命令
    linux 查看服务器序列号
    centos 7 lsof 安装使用
    Jenkins +svn +maven +tomcat+ ansible 自动化批量部署
    nginx 部署前期一定要关闭selinux
    yum 执行不了, 解决方法
    IIS发布网站
    使用TreeView 使用多选功能
    C#类和接口
    关于C#垃圾回收
  • 原文地址:https://www.cnblogs.com/jaywang/p/7774697.html
Copyright © 2011-2022 走看看