题目描述
给定一个正整数序列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
3
2 2 2
样例输出
(4+((1+2)+3))
19
3 6 10
((2+2)+2)
10
4 6
题解:就是石子合并,注意一些细节
#include<iostream> #include<cstring> #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; const int INF=0x3f3f3f3f; const int N=25; int n, a[N], cnt; int dp[N][N], L[N][N]; int vis[N][N]; void Print( int l, int r ) { if( l==r ) { printf( "%d", L[l][r] ); return; } printf( "(" ); Print( l, vis[l][r] ); printf( "+" ); Print( vis[l][r]+1, r ); printf( ")" ); a[++cnt]=L[l][r]; } int main() { scanf( "%d", &n ); for( int i=1; i<=n; i++ ) scanf( "%d", &a[i] ); for( int i=1; i<=n; i++ ) for( int j=i; j<=n; j++ ) L[i][j]=L[i][j-1]+a[j]; for( int len=1; len<n; len++ ) for( int i=1; i<=n; i++ ) { int j=i+len; if( j>n ) break; dp[i][j]=INF; for( int k=i; k<j; k++ ) if( dp[i][j]>=dp[i][k]+dp[k+1][j]+L[i][j] )//!!!一定要加等号,原因见样例2 dp[i][j]=dp[i][k]+dp[k+1][j]+L[i][j], vis[i][j]=k; } Print( 1, n ); putchar(10); if( n!=1 ) printf( "%d\n", dp[1][n] ); else printf( "%d\n", L[1][n] ); for( int i=1; i<=cnt; i++ ) { printf( "%d", a[i] ); if( i!=cnt ) putchar(32); else putchar(10); } return 0; }
[BZOJ1514]添加括号
最新推荐文章于 2020-06-04 22:18:34 发布