算法笔记_083:蓝桥杯练习 合并石子(Java)

时间:2023-02-13 18:22:08

目录

1 问题描述

2 解决方案

 


1 问题描述

问题描述
  在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数。求把所有石子合并成一堆的最小花费。
输入格式
  输入第一行包含一个整数n,表示石子的堆数。
  接下来一行,包含n个整数,按顺序给出每堆石子的大小 。
输出格式
  输出一个整数,表示合并的最小花费。
样例输入
5
1 2 3 4 5
样例输出
33
数据规模和约定
  1<=n<=1000, 每堆石子至少1颗,最多10000颗。

 


2 解决方案

本题主要考查动态规划法思想。

刚开始做的时候,使用贪心法求解,即每次选择相邻两个和为最小的一组合成堆,结果评分为10分,后来仔细想了一下,贪心法不能达到最优解,唯有使用动态规划法求解。

该题几乎和训练题集中的矩阵相乘一样,具体思想讲解,请参考本人另一篇博客:算法笔记_081:蓝桥杯练习 算法提高 矩阵乘法(Java)

 

 算法笔记_083:蓝桥杯练习 合并石子(Java)

具体代码如下:

import java.util.Scanner;

public class Main {
    
    public long getSum(long[] A, int a, int b) {
        long sum = 0;
        for(int i = a;i <= b;i++)
            sum += A[i];
        return sum;
    }
    
    public void printResult(long[] A) {
        if(A.length == 1) {
            System.out.println(A[0]);
            return;
        }
        long[][] dp = new long[A.length + 1][A.length + 1];
        for(int len = 2;len <= A.length;len++) {
            for(int i = 1, j = len;j <= A.length;i++,j++) {
                long min = Long.MAX_VALUE;
                for(int k = i;k < j;k++) {
                    if(min > dp[i][k] + dp[k + 1][j] + getSum(A, i - 1, j - 1))
                        min = dp[i][k] + dp[k + 1][j] + getSum(A, i - 1, j - 1);
                }
                dp[i][j] = min;
            }
        }
        System.out.println(dp[1][A.length]);
        return;
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        if(n < 1 || n > 1000)
            return;
        long[] A = new long[n];
        for(int i = 0;i < n;i++) {
            A[i] = in.nextLong();
        }
        test.printResult(A);
    }
}