tyvj1055 石子合并 【例题精讲】

    描述:
       设有N堆沙子排成一排,其编号为1,2,3,…,N(N<=300)。每堆沙子有一定的数量,可以用一个整数来描述,现在要将这N堆沙子合并成为一堆,每次只能合并相邻的两堆,合并的代价为这两堆沙子的数量之和,合并后与这两堆沙子相邻的沙子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同,如有4堆沙子分别为 1  3  5  2 我们可以先合并1、2堆,代价为4,得到4 5 2 又合并 1,2堆,代价为9,得到9 2 ,再合并得到11,总代价为4+9+11=24,如果第二步是先合并2,3堆,则代价为7,得到4 7,最后一次合并代价为11,总代价为4+7+11=22;问题是:找出一种合理的方法,使总的代价最小。输出最小代价。
   
 
    这是一道区间dp的入门题,我先简单谈谈区间dp,区间dp一般是学完背包问题来学的一类问题,许多人第一次见这道题和这道题的标程时,都不知道什么叫区间dp,但会发现一点,这道题的几重循环和以往的不大一样,并不是1到n或者是n到1这种简单的循环了,而是各种奇奇怪怪的限制条件,让人摸不到头脑,没办法阅读代码;就算是强行解释了,过一段时间也就忘了......
    所谓区间dp嘛,其实就是在一段区间上执行动态规划,那么如何执行呢?假如给出一段很长的区间,要处理每个点之间的关系,得到最终全局的最优解怎么办呢?
    区间dp的思想就是首先找一个长度为1的区间(一般称为元区间)作为初态然后开始处理问题,在处理时,我们 首先找一段长度为2的区间,通过循环使其在总区间上推进,同时处理点与点之间的关系 ,然后退回来,把区间长度加一再做上述工作,即每次都找了一段区间,在大区间上移动,从而解决问题;
    因此,区间长度便是动态规划的阶段!
    可能有的同学曾经迷糊过“动态规划那么多循环,到底哪个放外面,哪个放里面啊!”不急,其实,动态规划分为 阶段、状态和决策,三者应该从外到里循环。 至于什么是阶段状态决策,同学们可以通过刷题看题解慢慢领会。
    好,现在我们来处理这道题,一段石子,我们可以看成一段区间!思考一下,如果我要求第1-10个石子和第11-20个石子合并得最小花费,怎么办?可以想到,我们如果要合并这两段区间,前提是这两个区间内部已经完成了合并!好,那子问题就出来了!
    我们设f[l][r]是把从 点l到点r的石子合并的最小花费! 然后可以得到转移方程,f[l][r]=min(f[l][r], f[l][k]+f[k+1][r]);f[l][r]+=sum[r]-sum[l-1];   sum[i]数组表示前i个石子的重量的和;因为要加上本次合并的花费嘛!
    最后就是处理循环问题了,已知区间长为阶段,那么状态就是左端点和右端点,而决策就是不断地枚举合并点k的位置,找寻最优解!这道题完美结束!
 
上述内容部分参考李煜东学长的《算法竞赛进阶指南》
下面上代码!
 
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<string>
 7 #include<algorithm>
 8 #include<queue>
 9 using namespace std;
10 const int inf=0x3f;
11 int f[305][305],sum[305];
12 int val[305];
13 int n,m;
14 int main()
15 {
16     scanf("%d",&n);
17     for(int i=1;i<=n;i++){
18         scanf("%d",&val[i]);
19     }
20     memset(f,inf,sizeof(f));
21     for(int i=1;i<=n;i++){
22         f[i][i]=0;
23         sum[i]=sum[i-1]+val[i];
24     }
25     for(int len=2;len<=n;len++){//阶段,区间长度 
26         for(int l=1;l<=n-len+1;l++){//状态左端点 
27             int r=len-1+l;//右端点 
28             for(int k=l;k<r;k++){//决策,连接点k 
29                 f[l][r]=min(f[l][k]+f[k+1][r],f[l][r]);
30             }
31             f[l][r]+=sum[r]-sum[l-1];//本次合并的花费 
32         }
33     }
34     cout<<f[1][n]<<endl;
35     return 0;
36 }
View Code
 

 

 
 

转载于:https://www.cnblogs.com/Alan-Luo/articles/8723278.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值