[洛谷]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

说明/提示

范围在题目上有说明。


思路

本题难度:

第2问:普及+/提高

第1、3问:IOI+

首先第2问很像石子合并那题,用区间dp就好,然后一顿区间dp的套路即可:枚举区间长度,枚举区间左端点,求出区间右端点,枚举断点。

令dp[i][j]为区间[i,j]合并后能获得的最小和,则:

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1)

其中k是区间[i,j]中的一个断点,sum[j]-sum[i-1]为区间[i,j]和。

答案为[1,n]。

如何输出括号呢?

用la与ra数组记录一个数左右的括号数,然后就可以递归了。

#include <stdio.h>
#include <iostream>
#include <memory.h>
#define inf 2e9+7
#define int long long int 
using namespace std;
int a[21],n,s,dp[21][21],sum[21],point[21][21];
int la[21],ra[21],ans[21],cnt;
void dfs(int l,int r)
{
	if(l==r) return;
	la[l]++;
	ra[r]++;
	dfs(l,point[l][r]);
	dfs(point[l][r]+1,r);
	ans[++cnt]=sum[r]-sum[l-1];//合并一次能得到的值 
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	register int i,j,k,l;
	cin>>n;
	memset(dp,inf,sizeof(dp));
	for(i=1;i<=n;i++)
	{
		cin>>a[i];
		sum[i]=sum[i-1]+a[i];
		dp[i][i]=0;
	}
	for(l=2;l<=n;l++)
	{
		for(i=1;i+l-1<=n;i++)
		{
			j=i+l-1;
			for(k=i;k<=j;k++)
			{
				if(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]<=dp[i][j])
				{
					dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
					point[i][j]=k;//区间[l,r]取到最优解点的断点 
				}
			}
		}
	}
	dfs(1,n);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=la[i];j++)
		{
			cout<<'(';
		}
		cout<<a[i];
		for(j-1;j<=ra[i];j++)
		{
			cout<<')';
		}
		if(i!=n)
		{
			cout<<'+';
		}
	}cout<<endl;
	cout<<dp[1][n]<<endl;
	for(i=1;i<=cnt;i++)
	{
		cout<<ans[i]<<' ';
	}cout<<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值