石子合并 动态规划(直线型)
2017-03-05 18:17
302 查看
问题描述
在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数。求把所有石子合并成一堆的最小花费。
输入格式
输入第一行包含一个整数n,表示石子的堆数。
接下来一行,包含n个整数,按顺序给出每堆石子的大小 。
输出格式
输出一个整数,表示合并的最小花费。
样例输入
5
1 2 3 4 5
样例输出
33 //3+6+9+15=33
以下为解题经历:
第一反应,选择用贪心的思想去解题,因为题中提到只能合并相邻的石子,所以作一些改变,每次选择最小的石子堆,比较其左右两侧的石子,选择与较小的那个合并。
A[0..len] 取 Min(A) 将A[i] 与 Min(A[i+1],A[i-1]) 合并
其实此处,带着一些侥幸心理,隐隐觉得不会正确,因为在两两合并的过程中,上一次的合并结果会对下一次的合并造成影响,每一次并非都独立无关。
假如5堆石子,分别为7,6,5,7,100
•按照贪心,合并的过程如下:
7 6 5 7 100
7 11. 7 100
18. 7 100
25. 100
总得分=11+18+25+125=179
•另一种合并方案
7 6 5 7 100
13. 5 7 100
13 12. 100
25. 100
总得分=13+12+25+125=175
————————————————————————————————————
第二次解题,由于牵扯到不同情况下的不同解,第二反应是符合动态规划的思想,利用一次次的合并过程去更新所得的结果,以便下一次合并使用。
首先,列出石子 进行观察。A[i]为该堆中的石子个数
1.□ 2.□□ 3.□□□ 4.□□□□ 5.□□□□□
1.若石子有1堆,则无需合并,花费为0
2.若石子有2堆,则合并二者,花费为A[0]+A[1]
3.若石子有3堆,则有2种情况,①先合并A[0]、A[1]再与A[2]合并 ②先合并A[1]、A[2]再与A[0]合并
4.若石子有4堆,则有3种情况,...............
总之,在n堆中,有n-1种情况,而最后一次合并总是以某两堆合成一堆为结果。
即可将n堆划分成A[0...k] 与 A[k+1...n-1]合并,
而A[0...k] 与 A[k+1...n-1]已是最优花费,由各自向下分解所得。
(递归思想,但由于此次是二维的,即符合动态规划)
————————————————————————————————————
假设有5堆石子,则A[0...k] 与 A[k+1...n-1]的长度堆数可能为 1、4 2、3 3、2 4、1
只要其各自已为最优花费。
以上为递推向下的逻辑,在动态规划是由下而上,即5堆石子 需要长度为 2 3 4 的石子最优花费
如图。
![](http://img.blog.csdn.net/20170305182208989?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb25seTA2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
然后开始分析算法过程,首先,最外层循环 控制石子堆长度(len=2 to n),因为只有1堆时无需下续步骤
接着第二层循环,确定石子堆的起始堆号(i=0 to n-len)即为图中的下划线个数。
继续分析,发现还需要一层循环控制更新 A[0...k] 与 A[k+1...n-1]的花费price (k=i to j-1)
确定无遗漏后,开始编写代码过程,如下。
(注意,花费是每两两合并完的石子数依次相加,所以最后一次合并为合并堆数的总石子量)
——————————————————————————————————————————————、
由sum[i][j]表示第i堆石子到第j堆石子全部合并的总石子量。dp[i][j]为第i堆石子到第j堆合并的花费(随着动态规划的进行,会越来越越接近最优花费)dp[i][i]=0 (本身无需合并,不需花费)
即若仅两堆石子合并 则dp[i][j]=dp[i][i]+dp[j][j]+sum[i][j] = 0+0+sum[i][j]
以下用一维sum
进行存储,sum[i][j]=sum[j]-sum[i-1] (i!=0), sum[i][j]=sum[j] (i==0)
——————————————————————————————————————————————
完整代码如下:(时间限制:2.0s 内存限制:256.0MB 情况下只能得90分,一组运行超时)
import java.util.Arrays;
import java.util.Scanner;
public class 合并石子 {
public static void mergeStone(int[][] dp,int[] sum) {
int j=0,temp=0;
for (int i = 0; i < dp.length; i++) {
Arrays.fill(dp[i], Integer.MAX_VALUE);
dp[i][i]=0;
}
for (int len = 2; len <= dp.length; len ++) { //划分的石子堆数
for (int i = 0; i < dp.length-len+1; i++) {
j=i+len-1;
for (int k = i; k < j; k++) {
temp=dp[i][k]+dp[k+1][j]+sum[j]-(i==0?0:sum[i-1]);
dp[i][j]=Math.min(dp[i][j], temp);
[b]//System.out.println(i+","+j+" : "+dp[i][j]);
}
}
}
}
public static void main(String[] args) {
Scanner scam=new Scanner(System.in);
int n=scam.nextInt();
int[] sum=new int
;
int[][] dp=new int
;
for (int i = 0; i < sum.length; i++) {
if(i==0)
sum[i]=scam.nextInt();
else
sum[i]=sum[i-1]+scam.nextInt();
}
mergeStone(dp,sum);
System.out.println(dp[0][n-1]);
}
}
————————————————————————
[/b]
在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数。求把所有石子合并成一堆的最小花费。
输入格式
输入第一行包含一个整数n,表示石子的堆数。
接下来一行,包含n个整数,按顺序给出每堆石子的大小 。
输出格式
输出一个整数,表示合并的最小花费。
样例输入
5
1 2 3 4 5
样例输出
33 //3+6+9+15=33
以下为解题经历:
第一反应,选择用贪心的思想去解题,因为题中提到只能合并相邻的石子,所以作一些改变,每次选择最小的石子堆,比较其左右两侧的石子,选择与较小的那个合并。
A[0..len] 取 Min(A) 将A[i] 与 Min(A[i+1],A[i-1]) 合并
其实此处,带着一些侥幸心理,隐隐觉得不会正确,因为在两两合并的过程中,上一次的合并结果会对下一次的合并造成影响,每一次并非都独立无关。
假如5堆石子,分别为7,6,5,7,100
•按照贪心,合并的过程如下:
7 6 5 7 100
7 11. 7 100
18. 7 100
25. 100
总得分=11+18+25+125=179
•另一种合并方案
7 6 5 7 100
13. 5 7 100
13 12. 100
25. 100
总得分=13+12+25+125=175
————————————————————————————————————
第二次解题,由于牵扯到不同情况下的不同解,第二反应是符合动态规划的思想,利用一次次的合并过程去更新所得的结果,以便下一次合并使用。
首先,列出石子 进行观察。A[i]为该堆中的石子个数
1.□ 2.□□ 3.□□□ 4.□□□□ 5.□□□□□
1.若石子有1堆,则无需合并,花费为0
2.若石子有2堆,则合并二者,花费为A[0]+A[1]
3.若石子有3堆,则有2种情况,①先合并A[0]、A[1]再与A[2]合并 ②先合并A[1]、A[2]再与A[0]合并
4.若石子有4堆,则有3种情况,...............
总之,在n堆中,有n-1种情况,而最后一次合并总是以某两堆合成一堆为结果。
即可将n堆划分成A[0...k] 与 A[k+1...n-1]合并,
而A[0...k] 与 A[k+1...n-1]已是最优花费,由各自向下分解所得。
(递归思想,但由于此次是二维的,即符合动态规划)
————————————————————————————————————
假设有5堆石子,则A[0...k] 与 A[k+1...n-1]的长度堆数可能为 1、4 2、3 3、2 4、1
只要其各自已为最优花费。
以上为递推向下的逻辑,在动态规划是由下而上,即5堆石子 需要长度为 2 3 4 的石子最优花费
如图。
然后开始分析算法过程,首先,最外层循环 控制石子堆长度(len=2 to n),因为只有1堆时无需下续步骤
接着第二层循环,确定石子堆的起始堆号(i=0 to n-len)即为图中的下划线个数。
继续分析,发现还需要一层循环控制更新 A[0...k] 与 A[k+1...n-1]的花费price (k=i to j-1)
确定无遗漏后,开始编写代码过程,如下。
(注意,花费是每两两合并完的石子数依次相加,所以最后一次合并为合并堆数的总石子量)
——————————————————————————————————————————————、
由sum[i][j]表示第i堆石子到第j堆石子全部合并的总石子量。dp[i][j]为第i堆石子到第j堆合并的花费(随着动态规划的进行,会越来越越接近最优花费)dp[i][i]=0 (本身无需合并,不需花费)
即若仅两堆石子合并 则dp[i][j]=dp[i][i]+dp[j][j]+sum[i][j] = 0+0+sum[i][j]
以下用一维sum
进行存储,sum[i][j]=sum[j]-sum[i-1] (i!=0), sum[i][j]=sum[j] (i==0)
——————————————————————————————————————————————
完整代码如下:(时间限制:2.0s 内存限制:256.0MB 情况下只能得90分,一组运行超时)
import java.util.Arrays;
import java.util.Scanner;
public class 合并石子 {
public static void mergeStone(int[][] dp,int[] sum) {
int j=0,temp=0;
for (int i = 0; i < dp.length; i++) {
Arrays.fill(dp[i], Integer.MAX_VALUE);
dp[i][i]=0;
}
for (int len = 2; len <= dp.length; len ++) { //划分的石子堆数
for (int i = 0; i < dp.length-len+1; i++) {
j=i+len-1;
for (int k = i; k < j; k++) {
temp=dp[i][k]+dp[k+1][j]+sum[j]-(i==0?0:sum[i-1]);
dp[i][j]=Math.min(dp[i][j], temp);
[b]//System.out.println(i+","+j+" : "+dp[i][j]);
}
}
}
}
public static void main(String[] args) {
Scanner scam=new Scanner(System.in);
int n=scam.nextInt();
int[] sum=new int
;
int[][] dp=new int
;
for (int i = 0; i < sum.length; i++) {
if(i==0)
sum[i]=scam.nextInt();
else
sum[i]=sum[i-1]+scam.nextInt();
}
mergeStone(dp,sum);
System.out.println(dp[0][n-1]);
}
}
————————————————————————
[/b]
相关文章推荐
- 石子合并_直线型 动态规划
- 石子合并 动态规划(环形)
- 石子合并 帮果实 动态规划解法
- [动态规划] 洛谷P1063 能量项链 (石子合并)
- 【动态规划】不能移动的石子合并
- 石子合并(动态规划)详细解题报告(转)
- 石子合并问题--动态规划;贪心
- 石子合并问题 --动态规划--解法1
- poj1738 An old Stone Game 【GarsiaWachs算法】【动态规划】经典合并石子问题
- [WIKIOI 2102]石子合并2(动态规划)
- 石子合并(动态规划)详细解题报告
- poj 1738 An old Stone Game(区间dp 合并石子问题直线型)
- 动态规划:石子合并问题
- 【动态规划】石子合并 luogu-1880
- 石子合并(动态规划DP)
- 算法_动态规划_石子合并问题
- 石子合并(动态规划)详细解题报告
- 深入理解合并类动态规划——合并石子
- 石子合并问题(动态规划)
- 石子合并问题--动态规划;贪心