动态规划算法——2020美团校招合并金币算法

动态规划

以此谨记自己学习java心得
这几天为了笔试一直在牛客网刷题,分享一题动态规划的题目,想了好几天,今天开了几篇文章就开窍了。
首先我们来说说什么是动态规划算法。这里引用一下我参考的文章,讲的挺详细的

这是我参考的CSDN文章
看完上面这篇,我来简单介绍一下步骤吧。
1.一般算法题都是用二维数组来求解的,我这里比方说dp[][]
那这个数组dp[i][j]到底是什么含义,是弄清动态规划算法的前提,一般性的,我们都是题目问我们什么问题,我们就把这个意思转义给数组dp[i][j]。
2.动态规划的dp[i][j],在这个位置上,dp[i][j]是肯定和其他方位的位置有关系的,可能是和左侧有关,也可能是和上侧有关,也可能和左上侧有关,都是要看题目意思来确定的。
3.动态规划就是从小到大去求解,先确定某一个位置的最优解,比方说

array[i][j]=Math.min(array[i][j],array[i][k]+array[k+1][j]);

这里面的Math.min就是用来确定最小值的,在这里我们要考虑到数组越界的问题,那么必须初始化一些值,用来防止越界,以及循环后数组各个地方都有对应的值。
下面引入一道美团校招的编程题

有 N 堆金币排成一排,第 i 堆中有 C[i] 块金币。每次合并都会将相邻的两堆金币合并为一堆,成本为这两堆金币块数之和。经过N-1次合并,最终将所有金币合并为一堆。请找出将金币合并为一堆的最低成本。

其中,1 <= N <= 30,1 <= C[i] <= 100
第一行输入一个数字 N 表示有 N 堆金币
第二行输入 N 个数字表示每堆金币的数量 C[i]
输出一个数字 S 表示最小的合并成一堆的成本
输入例子1:
4
3 2 4 1

输出例子1:
20

输入例子2:
30
10 20 30 40 50 60 70 80 90 100 99 89 79 69 59 49 39 29 19 9 2 12 22 32 42 52 62 72 82 92

输出例子2:
7307

拿到题目,看到相邻两字,就考虑动态规划问题。
这里第一步确定dp[i][j]是什么意思,
我这里规定dp[i][j]就是从第i个位置开始到第j个位置结束,这一堆合并金币所需的最小成本。
第二步 确定各个点的关系式
打个比方说如果输入4个数 2 3 6 1(为了方便理解,我把下标定为从1开始 到4结束)
那我们就先比方说dp[1,3]也就是从第一个位置到第三个位置结束,那也就是2 3 6的最小成本数,我们有两种情况,
从2到3 是一堆 然后6是一堆; 2是一堆,3 到6是一堆。
那么我们这个dp[1][3]的公式就是dp[1][2]+dp[3][3]+2+3+6或者dp[1,1]+dp[2][3]+2+3+6;后面的2+3+6的意思是最后一次的成本数,也就是不管你是从哪个开始到哪个结束,最后一次的成本永远是从开始到结束的总和。比如说如果是从2 3为一堆先开始,那就是2 3 6=>5 6(5)=>11(5+11)
括号里是成本数。
那我们现在可以确定dp[i][j]的公式就是设置一个变量k
i<=k 第三步看看数组是否越界,发现在这个情况下,数组没有越界现象。
下面张贴代码,代码也写了注释

package Test0410;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class MoneyHeadNew {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n=scanner.nextInt();
//        int m=scanner.nextInt();
//        ArrayList list = new ArrayList<>();
        int array[][]=new int[n+1][n+1];
        int sum[]=new int[n+1];
        for (int i = 1; i < n+1; i++) {
            int a=scanner.nextInt();
            sum[i]=sum[i-1]+a;
        }
//        System.out.println(Arrays.toString(sum));
        for (int len = 2; len <=n ; len++) {//一堆有多少组成
            //比如len=2  就是组合二堆为1堆
            //len=3 就是组合三堆为1堆
            for (int i = 1; i <=n-len+1 ; i++) {//起点
                int j=i+len-1;//终点
                array[i][j]=Integer.MAX_VALUE;//为了一开始得到最小值
                int leastSum=sum[j]-sum[i-1];
                for (int k=i;k<j;k++) {
                    //转移方程就是因为合成一堆有不同的组合方法
                    //如2  3   6  1
                    //当len=2时 ,组合有2  3    3   6     6  1
                    //获取当前组合的最小值
                    //当len=3
                    //组合有 一堆是2 3 6    组合为2 3     6   组合为2    3 6
                    //比如组合2  3  6  就要获取2 3的最优解  3 6的最优解
                    //当Len=4
                    //组合有2 3 6 1    2 3 6     1       2   3  6  1
                    //因为之前已经得到了2 3 6 的最优解 和 3  6  1的最优解
                    //继续比较两者到底谁小


                    //这句话是真的核心
                    //从因为k刚开始是从i开始
                    //所以一开始也就是从i 开始到i结束所需成本+从i+1开始到j的成本
                    //然后循环后是从i到i+1结束的成本+从i+1+1到j的成本
                    //总的来说也就是从i到j 它要分成j-i个部分来相加求和找最小
                    //从前一段到后一段,从前一段到后一段
                    //前一段从0到最后
                    //后一段从最后到0


                    //sum 就是此次成本
                    //因为不管是从i到j哪一次
                    //最后的相加的成本一定是从i到j所有数字之和
                    //比方说2 3 6  最后一次肯定是2+3+6
                    //所以最后一定要加上11  不管是从2 3  还是 3 6  先合成一堆
                    array[i][j]=Math.min(array[i][j],array[i][k]+array[k+1][j]+leastSum);
                }
            }
        }
        System.out.println(array[1][n]);
    }
}

想了好几天的问题,今天终于想明白了。加油,多看几篇文章,写几题代码,你也能写出来的!

你可能感兴趣的:(动态规划算法——2020美团校招合并金币算法)