题目背景
给定一个正整数序列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个中间和,求出使中间和之和最小的添括号方法。
这个题,看上去就像是个区间DP,所以我使用类似石子合并的套路,n^3解决这个问题,但输出我不是很会,还是看了下题解才解决。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> using namespace std; int n,m,a[10006],f[1006][1006],max1=0,b[106][106],saber[106][106],tot=0,sum1[10006],zk[10006],yk[10006]; void dfs1(int x,int y) { if(x==y)return; zk[x]++,yk[y]++; int p=saber[x][y]; dfs1(x,p); dfs1(p+1,y); } void dfs2(int x,int y) { if(x==y)return; dfs2(x,saber[x][y]); dfs2(saber[x][y]+1,y); printf("%d ",sum1[y]-sum1[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) f[i][i]=0; for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { if(i==j) f[i][j]=0; else f[i][j]=10000006; } } for(int i=1;i<=n;++i) { sum1[i]=sum1[i-1]+a[i]; } for(int i=1;i<=n;++i) { for(int j=i;j<=n;++j) { b[i][j]=b[i][j-1]+a[j]; } } for(int len=2;len<=n;++len) { ++tot; for(int i=1;i<=n-len+1;++i) { int j=i+len-1; for(int k=i;k<j;++k) { if(f[i][j]>=f[i][k]+f[k+1][j]+b[i][j]) saber[i][j]=k; f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+b[i][j]); } } } dfs1(1,n); for(int i=1;i<=n;++i) { for(int j=1;j<=zk[i];++j) printf("("); printf("%d",a[i]); if(yk[i]==0&&i!=n) printf("+"); for(int j=1;j<=yk[i];++j) printf(")"); if(i!=n&&yk[i]!=0) printf("+"); } printf("\n%d\n",f[1][n]); dfs2(1,n); return 0; }