【日常学习】【区间DP】codevs1048 石子归并题解

迈不过友情╰ 2022-08-03 14:47 313阅读 0赞

题目描述 Description

有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。

输入描述 Input Description

第一行一个整数n(n<=100)

第二行n个整数w1,w2…wn (wi <= 100)

输出描述 Output Description

一个整数表示最小合并代价

样例输入 Sample Input

4

4 1 1 4

样例输出 Sample Output

18

石子归并,一道典型的区间DP

区间DP与传统DP不同,既不能顺推也不能倒推,区间DP划分阶段是按区间的长短划分,然后按起点枚举状态进行DP

本题与合并果子的区别在于,只能合并相邻的两堆,这就导致贪心策略是错误的。

核心代码为:

  1. for (int p=1;p<n;p++)
  2. {
  3. for (int i=1;i<=n-p;i++)
  4. {
  5. int j=i+p;
  6. f[i][j]=maxint;
  7. for (int k=i;k<j;k++)
  8. {
  9. f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
  10. }
  11. f[i][j]+=s[j]-s[i-1];
  12. }
  13. }

请注意,这个递推的复杂度为O(n³)

当然,也可以采用记忆化搜索的方案,Pas核心代码如下

  1. function dfs(i,j:longint):longint; //合并i..j
  2. var k:longint;
  3. begin
  4. if i=j then exit(0); // 初始f[i,j]:=0;
  5. if f[i,j]>0 then exit(f[i,j]); //已经求过
  6. f[i,j]:=maxlongint;/ /为求最小值准备
  7. for k:=i to j-1 do
  8. f[i,j]:=min(f[i,j],
  9. dfs(i,k)+dfs(k+1,j)+s[j]-s[i-1]);
  10. exit(f[i,j]); // dfs=f[i,j] 返回函数值
  11. end;

那么,我们直接上代码

  1. //codevs1048 石子归并 区间DP
  2. //copyright by ametake
  3. #include
  4. #include
  5. #include
  6. using namespace std;
  7. const int maxn=100+10;
  8. const int maxint=0x3f3f3f3f;
  9. int s[maxn],f[maxn][maxn];
  10. int x,n;
  11. int main()
  12. {
  13. scanf("%d",&n);
  14. for (int i=1;i<=n;i++)
  15. {
  16. scanf("%d",&x);
  17. s[i]=s[i-1]+x;
  18. }
  19. for (int p=1;p

——江头未是风波恶,别有人间行路难

发表评论

表情:
评论列表 (有 0 条评论,313人围观)

还没有评论,来说两句吧...

相关阅读

    相关 石子合并问题 (区间dp)

    石子合并问题是最经典的DP问题。首先它有如下3种题型: (1)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的

    相关 282 石子合并(区间dp)

    1. 问题描述: 设有 N 堆石子排成一排,其编号为 1,2,3,…,N。每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。每次只能合并相邻的两

    相关 51nod1021石子归并区间dp)

    题意:N堆石子摆成一条线。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。