Vijos 1038 添加括号 【区间DP】
不改变序列中每个元素在序列中的位置,把它们相加,并用括号记每次加法所得的和,称为中间和。
例如:
给出序列是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个中间和,求出使中间和之和最小的添括号方法。如果有多组最优解,考虑将括号尽可能往前放。
输出3行。
第一行,为添加括号的方法。
第二行,为最终的中间和之和。
第三行,为n-1个中间和,按照从左到右,从里到外的顺序输出。
分析:
这个题的第二问和合并石子没什么区别。
在一个操场上一排地摆放着N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个程序,计算出将N堆石子合并成一堆的最小得分。
对于第一问,肯定要记录括号在什么位置,我们采用递归+分治的思路,在计算最小值的时候,我们已经将区间( i , j )分成了两个子区间 ( i , p ) 和 ( p+1 , j ) ,于是枚举断点 p ,使得 f [ i ] [ j ] 最小,我们可以考虑,在每次更新 f [ i ] [ j ]时,都记录
g [ i ] [ j ] = p 为区间 ( i , j ) 要分出的两个括号的中间,即为分界线,从而:
输出 " ( " ==> 输出 ( i , p ) ==> 输出 " + " ==> 再输出 ( p+1 , j ) ==> 输出 " ) "
第三问同理。
注意:在记录 g [ i ] [ j ] 时,若出现 f [ i ] [ j ] == f [ i ] [ p ] + f [ p+1 ] [ j ] +sum [ j ] - sum[ i-1 ] 要更新它,因为题中红色的字告诉我们,括号越往前越好,而通过调试,我们发现 g [ i ] [ j ] 的更新只能变大,不能变小(即断点只能往后) ,并且前一段分得越少,括号越早结束,因此 最前面的 " ((( x + y ) + ... " 就越少。因此我们要把断点尽量往后放。
不明白的可以自己用下面的数据调试一下:(注意红字)
输入:
6
1 3 2 1 1 1
输出:
加上 " = " :
((1+3)+((2+1)+(1+1)))
23
4 3 2 5 9
不加:
((1+3)+(2+(1+(1+1))))
23
4 2 3 5 9
下面是参考代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int n,a[50],sum[50]={0}; int ff[50][50],f[50][50]={0}; void print_equa(int,int); void print_ans(); void print_sum(int,int); int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; sum[i]=sum[i-1]+a[i]; } memset(ff,0x3f,sizeof(ff)); for(int i=1;i<=n;i++) ff[i][i]=0; for(int l=2;l<=n;l++) for(int i=1;i<=n-l+1;i++) { int j=i+l-1; for(int p=i;p<j;p++) { if(ff[i][j]>=ff[i][p]+ff[p+1][j]+sum[j]-sum[i-1]) { ff[i][j]=ff[i][p]+ff[p+1][j]+sum[j]-sum[i-1]; f[i][j]=p; } } } print_equa(1,n); print_ans(); print_sum(1,n); return 0; } void print_equa(int l,int r) { if(l==r) { cout<<a[l]; return ; } cout<<"("; int k=f[l][r]; print_equa(l,k); cout<<"+"; print_equa(k+1,r); cout<<")"; } void print_ans() { cout<<endl<<ff[1][n]<<endl; } void print_sum(int l,int r) { if(!f[l][r]) return; int k=f[l][r]; print_sum(l,k); print_sum(k+1,r); cout<<sum[r]-sum[l-1]<<" "; }