添加括号 题解

添加括号

题目描述

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

输入样例

4
4 1 2 3

输出样例

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

这是一个类似合并果子的题目,把要运算的数看成一堆果子,把中间和看成合并两堆果子的体力,让你求得就是把所有果子合并起来的最小体力。

我们要求中间和,就可以把他分成两半再加上中间的和,但是中间的和要枚举,因为我们并不知道 i → j i \to j ij中间到底那个地方(k)最小。

所以设 d p [ i ] [ j ] dp[i][j] dp[i][j] i i i j j j的最小的中间和
d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i ] [ k ] + d p [ k + 1 ] [ j ] + p r e s u m [ j ] − p r e s u m [ i − 1 ] ) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + presum[j] - presum[i - 1]) dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+presum[j]presum[i1])
这里 p r e s u m presum presum a a a数组的前缀和。

for(int i = n - 1; i; i--)   //枚举左端点
    for(int j = i + 1; j <= n; j++)   //枚举右端点
    	dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + presum[j] - presum[i - 1]);   //这里是为了加上中间和

但是在代码里面我却比较了一下dp1[i][k] + dp1[k + 1][j] + presum[j] - presum[i - 1] <= dp1[i][j]

这里是为了在更新的时候有一个信号,如果是只有一个 d p 1 [ i ] [ j ] = m i n ( d p 1 [ i ] [ j ] , d p 1 [ i ] [ k ] + d p 1 [ k + 1 ] [ j ] + p r e s u m [ j ] − p r e s u m [ i − 1 ] ) dp1[i][j]=min(dp1[i][j], dp1[i][k] + dp1[k + 1][j] + presum[j] - presum[i - 1]) dp1[i][j]=min(dp1[i][j],dp1[i][k]+dp1[k+1][j]+presum[j]presum[i1])的话你不知道 d p 1 [ i ] [ j ] dp1[i][j] dp1[i][j]是在什么时候更新的,就没法更新 d p 2 [ i ] [ j ] dp2[i][j] dp2[i][j]

小于等于是为了在结果不改变的情况下决策点靠后(题目要求)

最后我们每更新一次dp1[i][j]都要记录一下当前这个状态是从哪里来的,因为最后要输出方案

#include <bits/stdc++.h>
using namespace std;
#define MAXN 55
#define INF 0x3f3f3f3f
int n, m;
int a[MAXN], presum[MAXN];
int cnt1[MAXN], cnt2[MAXN];
int dp1[MAXN][MAXN], dp2[MAXN][MAXN];
void dfs(int x, int y)
{
    
    
    if(x == y)
        return;
    cnt1[x]++, cnt2[y]++;
    dfs(x, dp2[x][y]);
    dfs(dp2[x][y] + 1, y);
}
void DFS(int x, int y)
{
    
    
    if(x == y)
        return;
    DFS(x, dp2[x][y]);
    DFS(dp2[x][y] + 1, y);    //按照更新的dp2,进行更新
    cout << presum[y] - presum[x - 1] << " ";
}
int main()
{
    
    
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for(int i = 1; i <= n; i++)
        dp1[i][i] = 0;
    for(int i = 1; i <= n; i++)
        presum[i] = presum[i - 1] + a[i];
    for(int i = n - 1; i; i--)
    {
    
    
        for(int j = i + 1; j <= n; j++)
        {
    
    
            dp1[i][j] = INF;
            for(int k = i; k < j; k++)
            {
    
    
                if(dp1[i][k] + dp1[k + 1][j] + presum[j] - presum[i - 1] <= dp1[i][j])   
                //这里是为了在更新的时候有一个信号,如果是只有一个dp1[i][j]=min(dp1[i][j], dp1[i][k] + dp1[k + 1][j] + presum[j] - presum[i - 1])的话你不知道你在什么时候更新的dp1[i][j]是在什么时候更新的,就没法更新
                {
    
    
                    dp1[i][j] = dp1[i][k] + dp1[k + 1][j] + presum[j] - presum[i - 1];
                    dp2[i][j] = k;
                }
            }
        }
    }
    dfs(1, n);
    for(int i = 1; i <= n; i++)
    {
    
    
        for(int j = 1; j <= cnt1[i]; j++)
            cout << '(';
        cout << a[i];
        if(!cnt2[i] && i != n)
            cout << '+';
        for(int j = 1; j <= cnt2[i]; j++)
            cout << ')';
        if(cnt2[i] && i != n)
            cout << '+';
    }
    //输出方案
    cout << endl << dp1[1][n] << endl;
    DFS(1, n);
    return 0;
}

完结撒花★,°:.☆( ̄▽ ̄)/$:.°★

猜你喜欢

转载自blog.csdn.net/CoderZeng/article/details/109045123