石子合并问题

合集下载

石子归并(动态规划)

石子归并(动态规划)

石子归并(动态规划)动态规划石子合并问题【石材加固】在一个圆形操场的四周摆放着n堆石子。

现要将石子有次序地合并成一堆。

规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。

尝试设计一个算法来计算将n堆石头合并成一堆的最小分数和最大分数。

[输入文件]包含两行,第1行是正整数n(1<=n<=100),表示有n堆石子。

第2行有n个数,分别表示每堆石子的个数。

【输出文件】输出两行。

第1行中的数字是最低分数;第2行中的数字是最高分。

[输入示例]44459[输出示例]4354【分析】起初,我以为贪心法可以解决这个问题,但事实上,由于必须有两个相邻的桩合并,贪心法不能保证每次都能得到所有桩中石头数量最多的两个桩。

例如,以下示例:6346542如果使用贪心法计算最小分数,则应为以下合并步骤:第一次合并3465422,3合并分数为5,第二次合并546545,4合并分数为9,第三次合并96545,4合并分数为9,第四次合并9699,6合并分数为15,第五次合并15915,9合并分数为24,总分=5+9+9+15+24=62但是如果采用如下合并方法,却可以得到比上面得分更少的方法:第一次合并3465423,4合并得分是7第二次合并765427,6合并得分是13第三次合并135424,2合并得分是6第四次合并13565,6合并得分是11第五次合并131113,11合并得分是24总得分=7+13+6+11+24=61因此,我们知道这个问题不能用贪婪的方法来解决。

在上面的例子中,相邻的两个石子数量分别为13和11的桩第五次合并。

第一堆、第二堆和第三堆(石块数量分别为3、4和6)以及第四、第五和第六堆(石块数量分别为5、4和2)组合四次后形成两堆石块。

所以问题归结为如何使两个子序列的N-2组合分数之和达到最优。

为了实现这一目标,我们将第一个序列分为两个:第一和第二堆形成子序列1,第三堆形成子序列2。

第8课 石子归并(C++)

第8课 石子归并(C++)

第8课石子归并【问题描述】在一个操场按次序从左到右摆放着n堆石子(n≤100),现要将石子有次序地合并成一堆。

规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分,求最小的得分总和。

【输入格式】第1行为石子堆数n;第2行为每堆的石子数,每两个数之间用一个空格分隔。

【输出格式】最小的得分总和。

【输入样例】6346542【输出样例】61样例说明:346542765421354213561311247+13+6+11+24=61分析问题对于动态规划类型的题目,首先要分析出问题的最优子结构。

前面介绍的动态规划类型都是“i的规模”问题由“i-1规模”或更小规模的子问题决策出来的。

如:1.“黑熊过河”中走到第i步的最优值是由第i-1和第i-2步的最优值决策出的:f[i]=max{f[i-l],f[i-2]}-Q+a[i]2.“防卫导弹”中到第i个导弹的最优值是由小于i的子问题最优值来决策出的:f[i]=max{f[j])+1(1≤j<i,h[j]≥h[i])3.“最长公共子序列”中,两个长度分别为i和j的字符串的最长公共子序列是由短一点的子问题最优值决策出来:O当i=0或j=0时f[i,j]=f[i-1,j-1]+1当i,j>O时,且xi =yi时Max(f[i,j-1],f[i-1,j])当i,j>O时,且xi ≠yi时然而这里的“i的规模问题”其实都是隐含地表示“从1到i的问题”,即从头开始到第i步的问题。

即f[i]实际上是f[1..i],f[i,j]实际上是f[1..i,1..j]。

本题是另一类动态规划问题,如果用类似前面介绍的方法分析会比较困难。

实际上,我们合并i堆石子时,可能先选择中间相邻的两堆石子,合并之后,虽然只有i-1堆石子,但并不是原先的1~i-1的子问题。

因此我们没有办法把一个f[1..i]的i堆石子问题直接转化成f[1..j](j<i)的子问题来决策。

动态规划-石子合并问题

动态规划-石子合并问题

动态规划-⽯⼦合并问题(1)问题描述 在⼀个圆形操场的四周摆放着 num 堆⽯⼦。

先要将⽯⼦有次序地合并成⼀堆。

规定每次只能选相邻的 2 堆⽯⼦合并成新的⼀堆,并将新的⼀堆⽯⼦数记为该次合并的耗费⼒⽓。

试设计⼀个算法,计算将 n 堆⽯⼦合并成⼀堆的最省⼒⽓数。

(2)算法思想 对于给定的 n 堆⽯⼦,当只有⼀堆时,不⽤搬,进⽽不耗费⼒⽓,然后依次计算出从 2 堆 ~ num 堆⽯⼦的最优解,并且堆数递增求最优解,依赖于上⼀步的解进⾏计算所得;(3)算法思路 此解法和矩阵连乘类似,我们知道矩阵连乘也是每次合并相邻的两个矩阵,那么⽯⼦合并可以⽤矩阵连乘的⽅式来解决。

设 dp[i][j] 表⽰第 i 到第 j 堆⽯⼦合并的最优值,sum[i][j] 表⽰第 i 到第 j 堆⽯⼦的所耗费的⼒⽓总数。

动规⽅程如下:(4)代码展⽰public class StoneMerge {/*** 记录⽯⼦堆的数量*/private static int num;/*** 记录每堆⽯⼦的重量*/private static int[] weight;/*** 记录⽯⼦堆断开的位置【便于计算局部最优解】*/private static int[][] location;/*** 记录⽯⼦堆局部最优解,以⾄于求得最终最优解【动规⽅程】*/private static int[][] dp;/*** 初始化数据*/private static void initData() {Scanner input = new Scanner(System.in);System.out.println("请输⼊⽯⼦堆数量:");num = input.nextInt();weight = new int[num];System.out.println("请输⼊每堆⽯⼦的重量:");for (int i = 0; i < weight.length; i++) {weight[i] = input.nextInt();}// 定义成 int 类型的⼆维数组,创建完每个元素直接初始化为 0dp = new int[num][num];location = new int[num][num];}/*** 计算最省最费⼒⽓值*/private static void dpFindMinStrength() {// 初始化 dp 数组for (int m = 0; m < num; m++) {dp[m][m] = 0; // ⼀堆⽯⼦,不⽤搬,耗费⼒⽓为 0}for (int r = 2; r <= num; r++) { // 从 2 堆依次到 num 堆,分别计算最优值for (int i = 0; i < num - r + 1; i++) { // 起始⽯⼦堆取值范围int j = i + r - 1; // 根据每次选取⽯⼦堆 r 和起始⽯⼦堆 i ,计算终⽌⽯⼦堆int sum = 0;for (int x = i; x <= j; x++) { // 计算从⽯⼦堆 i 到⽯⼦堆 j 合并时,最后两堆使⽤的⼒⽓总和 sumsum += weight[x];}// 根据动规⽅程,从局部最优解中计算当前从⽯⼦堆 i 到⽯⼦堆 j 合并所使⽤的的⼒⽓总和dp[i][j] = dp[i + 1][j] + sum; // 计算从 i ⽯⼦堆分开时,使⽤的⼒⽓总和location[i][j] = i; // 标记从第 i ⽯⼦堆分开位置for (int k = i + 1; k < j; k++) { // 需要统计从 k 【k ∈ (i, j)】⽯⼦堆分开,使⽤的⼒⽓总和int temp = dp[i][k] + dp[k + 1][j] + sum; // 计算从 k ⽯⼦堆分开时,使⽤的⼒⽓总和if (temp < dp[i][j]) {dp[i][j] = temp;location[i][j] = k;}}}}}/*** 输出*/private static void print() {System.out.println("动规数组【不同堆数合并⽯⼦所费⼒⽓】:");for (int i = 0; i < num; i++) {for (int j = 0; j < num; j++) {System.out.print(dp[i][j] + " ");}System.out.println();}System.out.println("不同堆数合并⽯⼦最省⼒⽓断开位置最优解:");for (int i = 0; i < num; i++) {for (int j = 0; j < num; j++) {System.out.print(location[i][j] + " ");}System.out.println();}}public static void main(String[] args) {// 初始化数据initData();// 计算最省最费⼒⽓值dpFindMinStrength();// 输出print();}}⽯⼦合并核⼼代码(5)输⼊输出请输⼊⽯⼦堆数量:4请输⼊每堆⽯⼦的重量:4 45 9动规数组【不同堆数合并⽯⼦所费⼒⽓】:0 8 21 430 0 9 270 0 0 140 0 0 0不同堆数合并⽯⼦最省⼒⽓分开位置最优解【下标从数组 0 开始分开】:0 0 1 20 0 1 20 0 0 20 0 0 0输⼊输出(6)总结 ⽯⼦合并问题完全提现了动态规划的核⼼思想,先求解⼦问题的解【⼦问题求解不相互独⽴,相互依赖】,然后从这些⼦问题的解中得到原问题的解,进⽽得到该问题的最优解。

石子合并问题

石子合并问题

石子合并问题
石子合并问题是最经典的DP问题。

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

求将这N堆石子合并成一堆的总花费最小(或最大)。

分析:当然这种情况是最简单的情况,合并的是任意两堆,直接贪心即可,每次选择最小的两堆合并。

本问题实际上就是哈夫曼的变形。

(2)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。

求将这N堆石子合并成一堆的总花费最小(或最大)。

分析:我们熟悉矩阵连乘,知道矩阵连乘也是每次合并相邻的两个矩阵,那么石子合并可以用矩阵连乘的方式来解决。

设dp[i][j]表示第i到第j堆石子合并的最优值,sum[i][j]表示第i到第j 堆石子的总数量。

那么就有状态转移公式:
代码如下:(直线)
return 0;
}
(3)问题(2)的是在石子排列是直线情况下的解法,如果把石子改为环形排列,又怎么做呢?
分析:状态转移方程为:
其中有:
代码如下:(环形)
#include <stdio.h>
#include <string.h>
#define INF 10000
#define N 205
int mins[N][N];
int maxs[N][N];
int sum[N],a[N];
int minval,maxval;
int n;
int min(int a,int b)
{
return a<b?a:b;。

动态规划思想:石子合并问题

动态规划思想:石子合并问题

动态规划思想:⽯⼦合并问题描述:在⼀个圆形操场的四周摆放着n 堆⽯⼦。

现要将⽯⼦有次序地合并成⼀堆。

规定每次只能选相邻的2 堆⽯⼦合并成新的⼀堆,并将新的⼀堆⽯⼦数记为该次合并的得分。

试设计⼀个算法,计算出将n堆⽯⼦合并成⼀堆的最⼩得分和最⼤得分。

开始以为通过贪⼼算法可能很快解决问题,可是是⾏不通的。

⾸先我们可以把这么堆⽯⼦看成⼀列我们假如5堆的⽯⼦,其中⽯⼦数分别为7,6,5,7,100•按照贪⼼法,合并的过程如下:每次合并得分第⼀次合并 7 6 5 7 100 =11 第⼆次合并 7 11 7 100=18 第三次合并 18 7 100 =25第四次合并 25 100 =125总得分=11+18+25+125=179•另⼀种合并⽅案每次合并得分 第⼀次合并 7 6 5 7 100 ->13第⼆次合并 13 5 7 100->12第三次合并 13 12 100 ->25第四次合并 25 100 ->125总得分=13+12+25+125=175显然利⽤贪⼼来做是错误的,贪⼼算法在⼦过程中得出的解只是局部最优,⽽不能保证使得全局的值最优。

如果N-1次合并的全局最优解包含了每⼀次合并的⼦问题的最优解,那么经这样的N-1次合并后的得分总和必然是最优的。

因此我们需要通过动态规划算法来求出最优解。

在此我们假设有n堆⽯⼦,⼀字排开,合并相邻两堆的⽯⼦,每合并两堆⽯⼦得到⼀个分数,最终合并后总分数最少的。

我们设m(i,j)定义为第i堆⽯⼦到第j堆⽯⼦合并后的最少总分数。

a(i)为第i堆⽯⼦得⽯⼦数量。

当合并的⽯⼦堆为1堆时,很明显m(i,i)的分数为0; 当合并的⽯⼦堆为2堆时,m(i,i+1)的分数为a(i)+a(i+1); 当合并的⽯⼦堆为3堆时,m(i,i+2)的分数为MIN((m(i,i)+m(i+1,i+2)+sum(i,i+2)),(m(i,i+1)+m(i+2,i+2)+sum(i,i+2)); 当合并的⽯⼦堆为4堆时......代码实现如下:1 #include<stdio.h>2#define N 1003/*4 *求合并过程中5 *最少合并堆数⽬6 **/7int MatrixChain_min(int p[N],int n)8 {9//定义⼆维数组m[i][j]来记录i到j的合并过成中最少⽯⼦数⽬10 //此处赋值为-11112int m[N][N];13for(int x=1;x<=n;x++)14for(int z=1;z<=n;z++)15 {16 m[x][z]=-1;17 }1819int min=0;2021//当⼀个单独合并时,m[i][i]设为0,表⽰没有⽯⼦22for(int g = 1;g<=n;g++) m[g][g]=0;2324//当相邻的两堆⽯⼦合并时,此时的m很容易可以看出是两者之和25for(int i=1;i<=n-1;i++)26 {27int j=i+1;28 m[i][j]=p[i]+p[j];29 }3031//当相邻的3堆以及到最后的n堆时,执⾏以下循环32for(int r=3; r<=n;r++)33for(int i=1;i<=n-r+1;i++)34 {35int j = i+r-1; //j总是距离i r-1的距离36int sum=0;37//当i到j堆⽯⼦合并时最后⾥⾯的⽯⼦数求和得sum38for(int b=i;b<=j;b++)39 sum+=p[b];4041// 此时m[i][j]为i~j堆⽯⼦间以m[i][i]+m[i+1][j]+sum结果,这是其中⼀种可能,不⼀定是最优42 //要与下⾯的情况相⽐较,唉,太详细了4344 m[i][j] = m[i+1][j]+sum;4546//除上⾯⼀种组合情况外的其他组合情况47for(int k=i+1;k<j;k++)48 {49int t=m[i][k]+m[k+1][j]+sum;50if(t<m[i][j])51 m[i][j] = t;5253 }54 }55//最终得到最优解56 min=m[1][n];57return min;585960 }6162/*63 *求合并过程中64 *最多合并堆数⽬65 **/6667int MatrixChain_max(int p[N],int n)68 {69int m[N][N];70for(int x=1;x<=n;x++)71for(int z=1;z<=n;z++)72 {73 m[x][z]=-1;74 }757677int max=0;78//⼀个独⾃组合时79for(int g = 1;g<=n;g++) m[g][g]=0;80//两个两两组合时81for(int i=1;i<=n-1;i++)82 {83int j=i+1;84 m[i][j]=p[i]+p[j];85 }8687for(int r=3; r<=n;r++)88for(int i=1;i<=n-r+1;i++)89 {90int j = i+r-1;91int sum=0;92for(int b=i;b<=j;b++)93 sum+=p[b];94 m[i][j] = m[i+1][j]+sum;9596for(int k=i+1;k<j;k++)97 {98int t=m[i][k]+m[k+1][j]+sum;99if(t>m[i][j])100 m[i][j] = t;101102 }103 }104105 max=m[1][n];106return max;107108109 }110int main()111 {112int stone[N];113int min=0;114int max=0;115int n;116 scanf("%d",&n);117for(int i=1;i<=n;i++)118 scanf("%d",&stone[i]);119120 min= MatrixChain_min(stone,n);121 max= MatrixChain_max(stone,n);122123//因为题⽬要求圆的原因,要把所有情况都要考虑到,总共有n种情况。

石子合并区间DP模板题

石子合并区间DP模板题

⽯⼦合并区间DP模板题题⽬链接:题意N堆⽯⼦摆成⼀条线。

现要将⽯⼦有次序地合并成⼀堆。

规定每次只能选相邻的2堆⽯⼦合并成新的⼀堆,并将新的⼀堆⽯⼦数记为该次合并的代价。

计算将N堆⽯⼦合并成⼀堆的最⼩代价。

例如:1 2 3 4,有不少合并⽅法1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)括号⾥⾯为总代价可以看出,第⼀种⽅法的代价最低,现在给出n堆⽯⼦的数量,计算最⼩合并代价。

输⼊第1⾏:N(2 <= N <= 100)第2 - N + 1:N堆⽯⼦的数量(1 <= A[i] <= 10000)输出输出最⼩合并代价样例输⼊41234样例输出19题解:这道题⽬如果其实我⾃⼰接触的时候还是很早的,我很早很早之前就在⼀场⽐赛当中接触到了这道题⽬,那是很久很久之前的⼀个春天,我⼤⼀的时候的⼀场校赛中,我唯⼀没有做出来TLE的题,因为当时还不知道记忆化搜索这个概念。

所以我当时的解法是⽤的dfs,没有使⽤记忆化进⾏剪枝。

这⾥的记忆化搜索其实可以对应动态规划的状态转移⽅程。

⾸先我们可以回到这道题⽬⾥⾯来,可以看到:区间[1..N]经过N-1次合并会获得最终的⼀个数,那么在经过N-2次合并的时候是会还剩2个数的,⽽且这两个数肯定还是连续的a[i]和a[i+1],所以,如果我们设dp[i][j]表⽰合并区间[i,j]的最⼩代价,那么:dp[i][j] = min(dp[i][k] + dp[k+1][j]) + sum(i,j) , 其中k=i,i+1,...,j-1这⾥的sum(i,j)表⽰a[i]+a[i+1]+...+a[j]。

那么dp[i][j]怎么求呢,我们可以通过定义⼀个函数dfs(i,j)来求得,但是可以通过记忆化搜索进⾏剪枝优化,其实就是写DP的时候记录的⼀些中间结果对应的重叠⼦问题。

石子合并dp表达式

石子合并dp表达式
石子合并问题可以使用动态规划来解决。

对于有N堆石子的情况,设
dp[i][j]表示将i至j之间的石子合并成一堆的最小花费。

初始时,对于任意i,都有dp[i][i]=0,因为合并一堆石子不需要花费。

对于区间[i,j],枚举合并点k,则该区间合并的最小花费为:dp[i][k]+ dp[k+1][j]+sum[i][j],其中sum[i][j]表示区间[i,j]中石子数量的和。

最终答案即为dp[1][n]。

对于有N堆石子,每次只能移动相邻的2堆石子合并的情况,最终的结果
为dp[1][n],表示合并全部石子的最小代价。

以上内容仅供参考,建议查阅相关资料文献,或者咨询数学领域专业人士,以获取更全面准确的信息。

石子合并问题实验报告

一、实验目的1. 了解石子合并问题的背景和意义;2. 掌握石子合并问题的解决方法;3. 提高实验操作能力和数据分析能力。

二、实验原理石子合并问题是一个经典的数学问题,主要研究如何将若干个石子合并成若干个尽可能大的石子堆。

该问题在现实生活中具有广泛的应用,如城市规划、资源分配等。

实验通过模拟石子合并过程,寻找最优的合并策略。

三、实验材料1. 石子若干;2. 纸和笔;3. 计算器。

四、实验步骤1. 准备实验材料,将石子随机分成若干堆;2. 记录每堆石子的数量;3. 按照一定的合并策略进行合并,如从数量最少的一堆开始合并;4. 记录每次合并后的石子堆数量和数量;5. 重复步骤3和4,直到所有石子合并成若干个石子堆;6. 分析实验结果,总结最优合并策略。

五、实验结果与分析1. 实验结果通过多次实验,发现以下几种合并策略:(1)从数量最少的一堆开始合并;(2)从数量最多的一堆开始合并;(3)从数量相差最小的一堆开始合并。

2. 实验分析(1)从数量最少的一堆开始合并:该策略在合并过程中可以逐渐减少石子堆的数量,但可能导致石子堆的体积差异较大。

(2)从数量最多的一堆开始合并:该策略在合并过程中可以保持石子堆的体积相对稳定,但可能导致石子堆的数量较多。

(3)从数量相差最小的一堆开始合并:该策略在合并过程中可以平衡石子堆的数量和体积,但需要花费更多的时间进行筛选。

综上所述,从数量相差最小的一堆开始合并是一种较为合理的合并策略。

六、实验结论1. 通过石子合并问题实验,了解了石子合并问题的背景和意义;2. 掌握了石子合并问题的解决方法,即从数量相差最小的一堆开始合并;3. 提高了实验操作能力和数据分析能力。

七、实验心得1. 在实验过程中,要注重观察和分析,以便找到最优的合并策略;2. 要善于总结经验,提高实验效率;3. 要注重团队合作,共同完成实验任务。

八、实验展望石子合并问题在现实生活中具有广泛的应用,未来可以从以下几个方面进行深入研究:1. 探讨不同合并策略的优缺点,寻找更优的合并策略;2. 将石子合并问题与其他实际问题相结合,如城市规划、资源分配等;3. 利用计算机技术,模拟石子合并过程,提高实验效率。

合并石子问题的算法讨论


1 +2 + 3 + = 8 + 4 211 6 7 7 7 4
算法三、GARSIAWACHS算法
算法三、GARSIAWACHS算法
【引理1】
【引理2】
【引理2】Βιβλιοθήκη 【引理2】【引理2】
【引理3】
【引理4】
GARSIAWACHS算法实现
GARSIAWACHS算法实现
平衡树维护优化 对于这个算法,我们只需要维护一个2-递减
序列就可以了,算法的精髓在于每次寻找一个
最小的k。先把n个数从后往前扫一遍,将不满 足2-递减性质的数加进树中,每次更新序列时, 通过平衡树来维护,最后算法时间复杂度为 O(nlogn)。
总结

在证明GarsiaWachs算法的过程中,我们多次用 到反证法的思想,通过假设反例成立,推出矛盾来 证明,这是一种思想,运用这种思想,能够解决很
么我们就把a[k]与a[k-1]合并,之后向前找最大 的一个满足a[j-1]>a[k]+a[k-1]的j,把合并后的值 a[k]+a[k-1]插入a[j]的前面,最后的答案就是每 次a[k]与a[k-1]合并的代价和。
算法三、GARSIAWACHS算法
原数列: 20 6 10 11 25 12
20 371011 25 12 20616 37 12 12 27 47 25 25 20 11 27 20
算法三、 GARSIAWACHS算法
假设第i个石子的权值为a[i]。 对于节点k,如果a[k-1]>a[k+1],那
么,称节点k满足2-递减性质。
算法三、 GARSIAWACHS算法
GarsiaWachs算法大意:

P5569[SDOI2008]石子合并(DP)

P5569[SDOI2008]⽯⼦合并(DP)满分做法:有⼀个专门解决这类问题的算法叫:GarsiaWachs算法。

算法流程:1.从序列开头往后找第⼀个位置i满⾜a[i]<=a[i+2]的点(在这⾥我们令a[n+1]和a[0]为正⽆穷)。

2.将a[i]和a[i+1]合并,并删除原来的两个数,后⾯⾃动补齐。

3.从i−1位置向前扫,找到第⼀个位置j满⾜a[j]>=a[i]+a[i+1],并把合并的值插⼊到j后⾯。

这样复杂度就降到了n²,⽤vector就可以达到nlogn了。

(可以⽤平衡树优化,但我不会┭┮﹏┭┮)。

#include<cstring>#include<queue>#include<cstdio>#include<iostream>#include<cmath>#include<algorithm>#include<vector>using namespace std;const int maxm=40007;typedef long long ll;int n;vector<ll> a;ll ans=0;ll work(){int k=a.size()-2;for(int i=0;i<=(int)a.size()-3;i++)//vector好像不能越界,i<=a.size()-3是错的,i<a.size()-2就对了{if(a[i]<=a[i+2]){k=i;break;}}ll tmp=a[k]+a[k+1];a.erase(a.begin()+k);//删除Ka.erase(a.begin()+k);//由于数组已经向左移了⼀个了,因此之前的A[k+1]跑到了A[k]的位置上,所以还是删除A[k]int pos=-1;for(int i=k-1;i>=0;i--){if(a[i]>tmp){pos=i;break;}}a.insert(a.begin()+pos+1,tmp);return tmp;}int main(){scanf("%d",&n);for(int i=1;i<=n;i++){ll x;scanf("%lld",&x);a.push_back(x);}for(int i=1;i<=n-1;i++){ans+=work();}printf("%lld\n",ans);return 0;}Processing math: 100%。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

石子合并问题
在一个圆形操场的四周摆放着n堆石子。

现要将石子有次序地合并成一堆。

规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。

试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。

分析:
假设有n堆石子需要合并,可以设计一个2*n-1个元素的数组来存储每堆石子的个数。

分析最优解的结构:假设有石头AiAi+1……Aj需要合并,简记为A[i,j].如果设最后一次合并发生在Ak与Ak+1之间(i<=k<j),则最后一个合并的得分为Ai……Aj堆石头的个数的总和记为totalValue(i,j).(不管你最后一次合并发生在哪个位
置,totalValue(i,j)的值都是一样的)因此总的得分等于A[i,k]的得分加上A[k+1,j]的得分再加上totalValue(i,j).
可以假设计算A[0,n-1]的一个最优次序所包含的计算子链A[0,k]和A[K+1,n-1]的次序也是最优的.
证明:
假设存在一个比计算A[0,k]的次序得分更少的次序,则用此次序来替换原来计算A[0,k]的次序,那么此时计算A[0,n-1]次序的得分就会比最优次序所得到的分数更少,这与假设相矛盾;同理可证明:计算A[0,n-1]的一个最优次序所包含的另一个计算子链A[k+1,n-1]的次序也是最优的!
综上所述,此题满足最优子结构性质,因此可以用动态规划算法来求解.
建立递归关系
设m[i][j]表示A[i,j]的计算结果.
当i=j时,表示只有一堆石头,不能合并,因此得分为零,所以m[i,j]=0;
当i<j时,可利用最优子结构性质来计算m[i][j],
m[i,j]=m[i,k]+m[k+1,j]+totalValue(i,j)(i<=k<j)
可用矩阵连乘的最优计算次序问题来求解这题.可选用自顶向下的备忘录算法或是自底向上的动态规划算法.
以下代码使用动态规划算法:
[cpp]view plaincopy
1.void MatrixChain(int *p,int n,int **m,int flag) //矩阵连乘算法
2.{
3.for(int i=0;i<n;i++)
4.m[i][i]=0;
5.for(int r=2;r<n;r++)
6.for(int i=0;i<n-r+1;i++)
7.{
8.int j=i+r-1;
9.int temp=totalValue(i,j,p);
10.m[i][j]=m[i+1][j]+temp;
11.for(int k=i+1;k<j;k++)
12.{
13.int t=m[i][k]+m[k+1][j]+temp;
14.if(!flag) //求最小得分
15.{
16.if(t<m[i][j])
17. m[i][j]=t;
18.}
19.else//求最大得分
20.if(t>m[i][j])
21.m[i][j]=t;
22.}
23.}
24.}
25.MatrixChain(inputNum,2*n-1,m,0); //计算最小得分
26.int resultMin=m[0][n-1];
27.for(i=1;i<=n-1;i++)
28.if(resultMin>m[i][n-1+i])
29.resultMin=m[i][n-1+i];
30.
31.MatrixChain(inputNum,2*n-1,m,1); //计算最大得分
32.int resultMax=m[0][n-1];
33.for(i=1;i<=n-1;i++)
34.if(resultMax<m[i][n-1+i])
35.resultMax=m[i][n-1+i];。

相关文档
最新文档