金币合并---区间动态规划问题(美团点评2020校招系统开发方向笔试题)

写在前面:

就当写个LOG,自19年4月更换工作,一直处于忙碌的状态,最近虽然复工了,但是由于yq影响和合同到期,工作并不多,所以有时间研究一下自己感兴趣的东西。顺便有问题请,大家指出来。 我写动态规划也是新手,大学数据几乎忘光所以很难抽象数学公式。

 有 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

析题:

 首先要搞懂这个问题,首先明白什么是区间动态规划,这里感谢下面这个文章的老哥,介绍给我什么是动态规划以及算法模板。

https://blog.csdn.net/qq_40772692/article/details/80183248

这里,我先介绍我的想法,我对动态规划这个算法没有上过具体的课程,也是靠平时数学基础来得到结果先看一下我的思路。可能百分之80%的都能想到。

按照提意,两两合并。假设每次只有两堆金币进行合并那么就会出现一下这几种情况。

定义参数

int[] jinbi=[3,2,4,1];

固定合并为jinbi[0],那么就会出现下面几种状况;

(1) jinbi[0]+jinbi[1] = 5 

      (jinbi[0]+jinbi[1])+jinbi[2]=9

      ((jinbi[0]+jinbi[1])+jinbi[2])+jibi[3] = 10

      结果为24

(2) jinbi[0]+jinbi[1] = 5

     jinbi[2]+jinbi[3] = 5

     (jinbi[0]+jinbi[1])+(jinbi[2]+jinbi[3]) = 10

     结果为20

以此类推,其他的项

推广到N项时:怎么抽象出来?

根据上面那篇文章的老哥介绍的核心思路我这里在赘述一下。

既然让我求解在一个区间上的最优解,那么我把这个区间分割成一个个小区间,求解每个小区间的最优解,再合并小区间得到大区间即可。所以在代码实现上,我可以枚举区间长度len为每次分割成的小区间长度(由短到长不断合并),内层枚举该长度下可以的起点,自然终点也就明了了。然后在这个起点终点之间枚举分割点,求解这段小区间在某个分割点下的最优解。

————————————————
版权声明:本文为CSDN博主「阿阿阿安」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_40772692/article/details/80183248

for(int len = 1;len<=n;len++){//枚举长度
        for(int j = 1;j+len<=n+1;j++){//枚举起点,ends<=n
            int ends = j+len - 1;
            for(int i = j;i<ends;i++){//枚举分割点,更新小区间最优解
                dp[j][ends] = min(dp[j][ends],dp[j][i]+dp[i+1][ends]+something);
            }
        }
    }

 

那么 根据资料可以变成这样,但是根据答案我有几处参数的具体定义不清楚 下面是代码

package wkx.leetCode;

import java.util.Scanner;

/**
 * 有 N 堆金币排成一排,第 i 堆中有 C[i] 块金币。
 * 每次合并都会将相邻的两堆金币合并为一堆,成本为这两堆金币块数之和。
 * 经过N-1次合并,最终将所有金币合并为一堆。
 * 请找出将金币合并为一堆的最低成本。
 * <p>
 * 其中,1 <= N <= 30,1 <= C[i] <= 100
 */
public class test1 {
    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] jinbi = new int[n];
        int[] sum = new int[n];
        for (int i = 0; i < n; i++) {
            jinbi[i] = in.nextInt();
            if (i == 0) sum[i] = jinbi[i];
            else sum[i] = sum[i - 1] + jinbi[i];
        }
        in.close();
        long temp = 0;
        long min = 0;
        long[][] dp = new long[n][n];

        for (int l = 1; l < n; l++) {//枚举长度
            System.out.println("l=" + l);
            for (int i = 0; i < n && i + l < n; i++) { //枚举起点 枚举终点ends<= n-1
                System.out.println("    i=" + i);
                min = dp[i][i] + dp[i + 1][i + l];//最小值
                System.out.println("    dp[i][i]:dp[" + i + "][" + i + "]=" + dp[i][i]);
                System.out.println("    dp[i + 1][i + l]:dp[" + (i + 1) + "][" + (i + l) + "]=" + dp[i + 1][i + l]);
                System.out.println("    min = " + min);
                for (int k = i + 1; k <= i + l - 1; k++) {//枚举分割点
                    System.out.println("        k=" + k);
                    temp = dp[i][k] + dp[k + 1][i + l];//其他步数的最小值
                    System.out.println("        dp[i][k]:dp[" + i + "][" + k + "]=" + dp[i][k]);
                    System.out.println("        dp[k + 1][i + l]:dp[" + (k + 1) + "][" + (i + l) + "]=" + dp[k + 1][i + l]);
                    System.out.println("        temp = " + temp);
                    System.out.println("            temp < min = " + (temp < min));
                    if (temp < min) {
                        min = temp;
                        System.out.println("                min = " + min);
                    }
                }
                System.out.println("        i > 0 =" + (i > 0));
                if (i > 0) {
                    dp[i][i + l] = min + sum[i + l] - sum[i - 1];
                    System.out.println("                min=" + min);
                    System.out.println("                sum[i + l]:sum[" + (i + l) + "]=" + sum[i + l]);
                    System.out.println("                sum[i - 1]:sum [" + (i - 1) + "]= " + sum[i - 1]);
                    System.out.println("                dp[i][i + l]:dp[" + i + "][" + (i + l) + "]=" + dp[i][i + l]);
                } else {
                    dp[i][i + l] = min + sum[l];
                    System.out.println("                min=" + min);
                    System.out.println("                sum[" + l + "]=" + sum[l]);
                    System.out.println("                dp[" + i + "][" + (i + l) + "]=" + dp[i][i + l]);
                }
            }
        }
        System.out.println(dp[0][n - 1]);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                System.out.print(dp[i][j] + " ");
            }
            System.out.println();
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值