蓝桥杯-和并石子--区间DP

来源:互联网 发布:洲际导弹 知乎 编辑:程序博客网 时间:2024/06/05 03:30
问题描述
  在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数。求把所有石子合并成一堆的最小花费。
输入格式
  输入第一行包含一个整数n,表示石子的堆数。
  接下来一行,包含n个整数,按顺序给出每堆石子的大小 。
输出格式
  输出一个整数,表示合并的最小花费。
样例输入
5
1 2 3 4 5
样例输出
33
数据规模和约定

  1<=n<=1000, 每堆石子至少1颗,最多10000颗。


假如有5堆石头:7 6 5 7 30;

最后合成一堆的石头,肯定是由两堆石头合起来的;a,b两坨;a这坨可能是由原来的1堆,2堆,3堆,4堆合起来的;b坨对应的就是4,3,2,1坨。。

我们需要把这些情况都算出来:并取这四中情况最小的作为选择;

1+4;2+3;3+2;4+1;因为是相邻才能合并;所以1+4和4+1并不是一种情况;可以把1+4理解为a是第一堆,b是第二堆到第五堆。。

怎么算呢?假如是a=1;b=4这种情况;a=1,就是第一坨石头数量嘛:7;b坨又可以分为两坨c,d:1+3;2+2;3+1;

这些情况也需要算出来。。并选出这其中花费最小的组合;

怎么算。。假如c,d是1+3组合;这里c=1;自然是第二堆石头:6;d坨又可能分为e,f:1+2;2+1;算出这两种情况,并选出花费最小的组合;

怎么算!假如e,f为1+2的组合;e为1,即第三堆:5;f为2,自然为1+1;分别为第四堆和第五堆数量了;


用dp[i][j]记录从第i堆合并到第j堆的最小花费

所以需要从底层往上走;先算出两个两个一堆的dp[0][1],dp[1][2],dp[2][3],dp[3][4];

这样才能算三个三个一堆的:dp[0][2]=Min{dp[0][0]+dp[1][2] , dp[0][1],dp[2][2]}; 依次dp[1][3],dp[2][4];

算四个四个同一堆的:dp[0][3]=Min{dp[0][0]+dp[1][3] , dp[0][1]+dp[2][3] , dp[0][2] + dp[3][3]};依次dp[1][4];

最后算5个一堆的!dp[0][4] = Min{dp[0][0]+dp[1][4] , dp[0][1]+dp[2][4] , dp[0][2]+dp[3][4] , dp[0][3]+dp[4][4]};

你看最后的dp[0][4] 是不是就是 1+4;2+3;3+2;4+1;这四种组合选出最小的意思~

状态转移方程:

dp[i][j] = Min{dp[i][k] + dp[k+1][j]} + sum[i][j];(i!=j)(k从i到j-1);

其中sum[i][j]为i到j的石头总量;为啥还有这个?你看状态里不有两堆石头嘛,dp[i][k] ,dp[k+1][j] 这两堆石头合并还要花费啊。



import java.util.Scanner;public class 提高算法合并石子DP {static int sum;public static void main(String[] args) {// TODO Auto-generated method stubScanner input = new Scanner(System.in);int n = input.nextInt();int stone[] = new int[n];int dp[][] = new int[n][n];//dp[i][j]储存从i到j的石头合并需要的最少花费for (int i = 0; i < n; i++) {if (i==0) {stone[i] = input.nextInt();}else {stone[i] = stone[i-1] + input.nextInt();}}for (int i = 0; i < n; i++) {dp[i][i] = 0;//从i到i的石头合并花费为0}for (int l = 2;  l <= n; l++) {//遍历每种长度l从2->nfor (int start = 0; start < n-l+1; start++) {//遍历l长度下的每个起点int end = start + l - 1;//对于此时的start对应的终点为enddp[start][end] = Integer.MAX_VALUE;for (int k = start; k < end; k++) {//对于dp[start][end]按k分割,有K中分割方案;if (dp[start][end]>dp[start][k]+dp[k+1][end]) {dp[start][end]=dp[start][k]+dp[k+1][end];//依次比较,取最小的方案;}}//上面部分是分割了两部分;到这了dp[start][end]等于分割到两部分的花费,还需要加上把这两部分合并的花费//也就是从start 到end 的石头数量int sum = 0;if (start==0) {sum = stone[end];}else {sum = stone[end] - stone[start-1];}dp[start][end] += sum;//加上从start到end的石头数量}}System.out.println(dp[0][n-1]);}}


0 0
原创粉丝点击