zoukankan      html  css  js  c++  java
  • 区间动态规划

    石子合并

    现在有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.
  • 相关阅读:
    可以说微软做的用户体验还不如这家泡菜厂
    如何在Visual Studio 工程之间共享静态内容 (js, css, img, etc.)
    使用JavaScript序列化任意复杂的对象
    使用ReSharper打造团队代码检查流程
    分享我们项目中基于EF事务机制的架构
    3句英文让你成为专业的欧美外包开发者
    linux 开机自启动脚本
    nginx 配置简单网站项目(linux下)
    python2 与 python3 如何实现共存
    centos 安装mysql
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/7202296.html
Copyright © 2011-2022 走看看