码迷,mamicode.com
首页 > 其他好文 > 详细

关于最小代价子母树

时间:2016-07-12 22:50:21      阅读:318      评论:0      收藏:0      [点我收藏+]

标签:

第一次尝试写动态规划(Dynamic Planning)=

问题如下:

-------------------------------------------------------------------------------------------------------------------------

最小代价子母树

设有一排数,共n个,例如:22 14 7 13 26 15 11.任意2个相邻的数可以进行归并,归并的代价为该两个数的和,经过不断的归并,最后归为一堆,而全部归并代价的和称为总代价,给出一种归并算法,使总代价为最小.
输入、输出数据格式与“石子合并”相同。
输入样例:
4
12 5 16 4

-------------------------------------------------------------------------------------------------------------------------

 

为了说明算法的过程,我们先分析一些简单的情况:

 

(1)n=2:

 

技术分享技术分享技术分享

 

当n=2时,仅有1种堆法,因此总的归并代价为2堆沙子的和。

 

(2)n=3:

 

技术分享技术分享 
 当n=3时,有2种堆法。

 

第1种堆法的总代价为20+24,第2种堆法的总代价为11+24。由此可见,最后一次的归并代价为全部沙子数量的和,对任何归并方案都是相同的,因此总的代价将取决于第一次归并,第1种方法的第1次归并代价为20,第2种方法的第1次归并代价为11,因此第种方法比第1种好。

 

(3)n=4:

 

技术分享技术分享技术分享技术分享
方法a总代价:20+34+40=94       方法b总代价:21+34+40=95    方法c总代价:20+20+40=80      方法d总代价:21+27+40=88
技术分享

 

方法e总代价:20+27+40=87

 

当n=4时,共有5种归并的方法,这5种方法可以分为3类:

 

1)包括a和b基本方法是先归并前面的3堆,在归并最后一堆,由于归并最后一堆的方法是相同的,所以在归并前3堆时不同的方法将产生不同的结果。a的总代价为54,b的总代价为55,所以第1类方法中a比b优;

 

2)仅有1种方法,c,分别归并2堆的代价分别为20,20,相加为40,共80;

 

3)包括d和e,基本方法是先归并后面3堆,再归并第1堆,由于归并第1堆的方法是相同的,所以在归并后3堆时不同的方法将产生不同的结果,由上面的分析可知e比d优;

 

由此我们可以发现,将每一层的最优解找出来,就可以用组合的方法列举出每两个解得到的解,因为每一个结点都是最优解,所以各个最优解组合出来的解也必定为最优解之一,再从中找出最小的一个就是该根结点所对应的沙堆的最小归并方案。

 

用二维数组表示为:

 

技术分享

 

 

刚开始犯了很多错,

比如先开始是这么写的(一脸蒙蔽):

 

1 for (int i=1;i<=n;i++)
2         for (int j=i+1;j<=n;j++)
3              for (int k=i;k<=j-1;k++)
4                f[i][j]=(f[i][j]>f[i][k]+f[k+1][j]+g[i][j])?f[i][k]+f[k+1][j]+g[i][j]:f[i][j];//未考虑递推关系

 

手动挥手.jpg

 

正解如下:

 1 #include "iostream"
 2 #include "cstdio"
 3 #include "queue"
 4 #define qwq 21000000
 5 int n,m,q;
 6 using namespace std;
 7 int f[1000][1000];
 8 int g[1000][1000];
 9 int gra[1000],minn=-2100000;
10 int main()
11 {
12     int n;
13     cin>>n;
14     for (int i=1;i<=n;i++)
15         cin>>gra[i];
16         
17     for (int i=1;i<=n;i++)
18         g[i][i]=gra[i];            
19          
20     for (int i=1;i<=n;i++)
21         for (int j=i+1;j<=n;j++)
22             g[i][j]=g[i][j-1]+gra[j];
23             
24     for (int i=1;i<=n;i++)
25         for (int j=1;j<=n;j++)
26             f[i][j]=(i!=j)?qwq:0; //i -> j(i)的代价为 +∞(0) 
27             
28     for (int p=2;p<=n;p++)//穷举堆数//因为是递推(Recursion)所以从小堆数向大堆数
29      for (int i=1;i<=(n-p+1);i++)//一段的确定性(Certainty) 列举起点
30      {
31          int j=i+p-1;            //终点(Destination)
32          for (int k=i;k<=j-1;k++)//穷举隔开位置 //注意超界所致的溢出(Overflow)
33              f[i][j]=(f[i][j]>f[i][k]+f[k+1][j]+g[i][j])?f[i][k]+f[k+1][j]+g[i][j]:f[i][j];//三目运算符不清楚的可以百度,下面也有简介
34         }        //正确写出状态转移方程//无后效性 (Unfollow-up Effect)
35     cout<<f[1][n]<<endl;
36 }

动态规划真的是个神奇的东西,

它适合求解多阶段(状态转换)决策问题的最优解,也可用于含有线性或非线性递推关系的最优解问题。

但其实它并不全能,

 

1,最优化原理

2,无后效性

必须满足这两个条件才能用,

当然,

写动态规划最主要的是写出状态转移方程,

其次是边界条件。

附一:

三目运算符普遍的用处就是减少if语句的使用

例如:

 1 i=(括号条件成立与否)?(成立):(不成立); 

 

关于最小代价子母树

标签:

原文地址:http://www.cnblogs.com/RoitAGI/p/5665040.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!