区间动态规划

石子合并

现在有n块石头,多多要把这n个石头进行合并
每一次合并,多多可以把相邻个石子合并到一起,得分等于两个石头的重量之和。

可以看出,所有的石子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共的得分等于每次合并石头质量之和。
求得分最少是多少,最多是多少? (n<=100)

样例输入#1

4
4 4 5 9

样例输出#1

43
54

样例解释#1

得分最大情况:

第一步,多多先把5和9合并,得分为14,各石子数变成:4 4 14

第二步,多多先把14和4合并,得分为18,各石子变成:4 18

第三步,多多不得不把4和18合并,得分为22,,只剩下一堆石子:22

各得分和为54,可以证明分数最大为54;

得分最小情况:

第一步,多多先把4和4合并,得分为8,各石子变成:8 5 9

第二步,多多先把8和5合并,得分为13,各石子变成:13 9

第三步,多多不得不把13和9合并,得分为22,,只剩下一堆石子:22

各得分和为43,可以证明分数最小为43;

【题解】

可以知道,这道题是经典的区间dp问题;我们设dp[i][j]为i~j中操作的最大得分(以最大为例)

这里的sum[i,j]表示原数列中的i+i+1+.......+j的值,我们可以通过O(n^2)的预处理来求出sum

第0阶段:dp[1][1],dp[2][2],dp[3][3],dp[4][4] 因为一开始还没有合并,所以这些值应该全部为0。

第1阶段:两两合并过程如下,其中sum(i,j)表示石头的数量,即从i开始数j个数的和

                  dp[1,2]=dp[1,1]+dp[2,2]+sum[1,2];

     dp[2,3]=dp[2,2]+dp[3,3]+sum[2,3];

     dp[3,4]=dp[3,3]+dp[4,4]+sum[4,4];

第2阶段:三三合并可以拆成两两合并,拆分方法有两种,前两个为一组或后两个为一组

         dp[1,3]=dp[1,2]+dp[3,3]+sum[1,3]或dp[1,3]=dp[1,1]+dp[2,3]+sum[1,3];取其最优

    dp[2,4]=dp[2,2]+dp[3,4]+sun[2,4]或dp[2,4]=dp[2,3]+dp[3,3]+sum[2,4];取其最优

第3阶段,四四合并可以拆成三三合并,拆分方法有一组,

         dp[1,4]=dp[1,1]+dp[2,4]+sum[1,4]或dp[1,2]+dp[3,4]+sum[1,4]或dp[1,3]+dp[4,4]+sum[1,4]取其最优;

于是,简单的距离后我们发现一个事实:每一个dp式都是由三个部分构成的(i≠j时)

                               dp[i,j]:dp[i,k]+dp[k+1,j]+sum[i,j](i,j需要枚举)

从而得出下面的转移方程:

   dp[i,j]:=max(dp[i,j],dp[i,k]+dp[k+1,j]+sum[i,j]);    | i<=k<j  |

答案就是dp[1,n]

代码实现比较简单:

石子合并(最大得分):

uses math;
var n,i,j,len,k,a,xx,yy:longint;
    sum,dp:array[0..1000,0..1000]of longint;
    s:array[0..1000]of longint;
begin
 readln(n);
 for i:=1 to n do begin
  read(a);
  s[i]:=s[i-1]+a;
 end;
 for i:=1 to n do
  for j:=i to n do
   sum[i,j]:=s[j]-s[i-1];
 for i:=1 to n do dp[i,i]:=0;
 for len:=1 to n do begin
  i:=0; j:=0;
  while (i<=n)and(j<=n) do begin
   inc(i); j:=len+i;
   for k:=i to j-1 do
   dp[i,j]:=max(dp[i,j],dp[i,k]+dp[k+1,j]+sum[i,j]);
  end;
 end;
 writeln(dp[1,n]);
end.

石子合并最小得分:

//注意赋初值!!
uses math;
var n,i,j,len,k,a,xx,yy:longint;
    sum,dp:array[0..1000,0..1000]of longint;
    s:array[0..1000]of longint;
begin
 readln(n);
 for i:=1 to n do begin
  read(a);
  s[i]:=s[i-1]+a;
 end;
 for i:=1 to n do
  for j:=i to n do
   sum[i,j]:=s[j]-s[i-1];
 for i:=1 to n do
  for j:=1 to n do dp[i,j]:=maxint;
 for i:=1 to n do dp[i,i]:=0;
 for len:=1 to n do begin
  i:=0; j:=0;
  while (i<=n)and(j<=n) do begin
   inc(i); j:=len+i;
   for k:=i to j-1 do
   dp[i,j]:=min(dp[i,j],dp[i,k]+dp[k+1,j]+sum[i,j]);
  end;
 end;
 writeln(dp[1,n]);
end.

 

posted @ 2017-07-18 19:34  ljc20020730  阅读(163)  评论(0编辑  收藏  举报