Vijos 1038 添加括号 【区间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个中间和,求出使中间和之和最小的添括号方法。如果有多组最优解,考虑将括号尽可能往前放

输出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
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]<<" ";
}
View Code

 

posted @ 2017-07-19 16:02  Captain_fcj  阅读(253)  评论(0编辑  收藏  举报