LeetCode题解:如何求解金矿问题(动态规划)

题目

很久很久以前,有一位国王拥有5座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人人数也不同。例如有的金矿储量是500kg黄金,需要5个工人来挖掘:有的金矿储量是200kg黄金,需要3个工人来挖掘…

如果参与挖矿的工人的总数是10。每座金矿要么全挖,要么不挖,不能派出一半人挖取一半的金矿。要求用程序求出,要想得到尽可能多的黄金,应该选择挖取哪几座金矿?

总共10个工人,A:400kg黄金/5人 B:500kg黄金/5人 C:200kg黄金/3人 D:300kg黄金/4人 E:350kg黄金/3人

n/w 1个工人 2个工人 3个工人 4个工人 5个工人 6个工人 7个工人 8个工人 9个工人 10个工人
400kg黄金/5人
500kg黄金/5人
200kg黄金/3人
300kg黄金/4人
350kg黄金/3人
  • 解决方法

    1. 贪心算法:依次求得局部最优解,最终得到全局最优解

      1. 按照金矿的性价比从高到低进行排序,有限选择性价比最高的金矿来挖掘,然后选择性价比第2的…
      2. 按照贪心算法的思路得出来的最佳金矿收益是350+500即850kg黄金
        == 按照这种想法是局部最优,但是全局未必是最优的
    2. 动态规划:把复杂的问题简化成规模较小的子问题,再从简单的子问题自底向上一步一步递推
      动态规划的要点:确定全局最优解和最优子结构之间的关系,以及问题的边界

      原问题分解成子问题进行求解(类似于背包问题

  • 动态规划的状态转移方程式

    我们把金矿数量设为n,工人数量设为w,金矿的含金量设为数组g[ ],金矿所需开采人数设为数组p[ ]. 设F(n, w)为n个金矿、w个工人时的最优收益函数,那么状态转移方程式如下。

    F(n,w) = 0 (n=0或w=0)

    问题边界,金矿数为0或工人数为0的情况。

    F(n,w)= F(n-1,W) (n≥1, w<p[n-1])

    当所剩工人不够挖掘当前金矿时,只有一种最优子结构。

    F(n,w) = max(F(n-1,w), F(n-1,w-p[n-1])+g[n-1]) (n≥1, w≥p[n-1])

    在常规情况下,具有两种最优子结构(挖当前金矿或不挖当前金矿)。

  • 相同的颜色代表了方法被传入相同的参数
    在这里插入图片描述

  • 自底向上求解

w < p[n-1] : F(n,w)= F(n-1,W) (n≥1, w<p[n-1])
w ≥ p[n-1] : F(n,w) = max(F(n-1,w), F(n-1,w-p[n-1])+g[n-1])

例如:F(2,10) = max(F(2-1,10),F(2-1,10-5)+500) = max(F(1,10),F(1,5)+500) = max(400,400+500) = 900

n/w 1个工人 2个工人 3个工人 4个工人 5个工人 6个工人 7个工人 8个工人 9个工人 10个工人
400kg黄金/5人 0 0 0 0 400 400 400 400 400 400
500kg黄金/5人 0 0 0 0 500 500 500 500 500 900
200kg黄金/3人 0 0 200 200 500 500 500 700 700 900
300kg黄金/4人 0 0 200 300 500 500 500 700 800 900
350kg黄金/3人 0 0 350 350 500 550 650 850 850 900
  • 代码实现
package some_problem;
/**
 * Copyright (C), 2019-2020
 * author  candy_chen
 * date   2020/7/30 16:00
 * version 1.0
 * Description: 测试
 */

/**
 *
 */
public class GetBestGoldMining {
    
    

    /**
     * 获得金矿最优收益
     * @param w 工人数量
     * @param n 可选金矿数量
     * @param p 金矿开采所需的工人数量
     * @param g 金矿储量
     * @return
     */
     //递归进行求解
    public static int getBestGoldMining(int w,int n,int[] p,int[] g){
    
    
        if (w ==0||n==0){
    
    
            return 0;
        }
        if (w<p[n-1]){
    
    
            return getBestGoldMining(w,n-1,p,g);
        }
        return Math.max(getBestGoldMining(w,n-1,p,g),getBestGoldMining(w-p[n-1],n-1,p,g) + g[n-1]);
    }

    /**
     * 获得金矿最优收益
     * @param w 工人数量
     * @param p 金矿开采所需的工人数量
     * @param g 金矿数量
     * @return
     */
     //递归做了很多重复的计算,当金矿越来越多,递归层次越来越深,重复调用也就越来越多,无谓的调用必然会降低程序的性能
     //利用双循环来填充一个二维数组,时间复杂度和空间复杂度都是O(nw)
    public static int getBestGoldMiningV2(int w,int[] p,int[] g){
    
    
        //创建表格
        int[][] resultTable = new int[g.length+1][w +1];
        //填充表格
        for (int i =1;i<= g.length;i++){
    
    
            for (int j=1;j<=w;j++){
    
    
                if (j<p[i-1]){
    
    
                    resultTable[i][j] = resultTable[i-1][j];
                }else{
    
    
                    resultTable[i][j] = Math.max(resultTable[i-1][j],resultTable[i-1][j-p[i-1]] + g[i-1]);
                }
            }
        }
        //返回最后1个格子的值
        return resultTable[g.length][w];
    }

	// 并不需要保存整个表格,无论金矿多少座,我们只保存1行的数据即可,在计算下一行时,要从右向左统计,把旧的数据一个一个替换掉
	//时间复杂度是O(nw) 空间复杂度是O(n)
	 /**
     * 获得金矿最优收益
     * 
     * @param w 工人数量
     * @param p 金矿开采所需的工人数量
     * @param g 金矿数量
     * @return
     */
    public static int getBestGoldMiningV3(int w,int[] p,int[] g){
    
    
        //创建当前结果
        int[] results = new int[w+1];
        //填充一维数组
        for (int i=1;i<= g.length;i++){
    
    
            for (int j=w;j>=1;j--){
    
    
                if (j>=p[i-1]){
    
    
                    results[j] = Math.max(results[j],results[j-p[i-1]] + g[i-1]);
                }
            }
        }
        //返回最后一个格子的值
        return results[w];
    }


    public static void main(String[] args) {
    
    
        int w = 10;
        int[] p = {
    
    5,5,3,4,3};
        int[] g = {
    
    400,500,200,300,350};
        System.out.println("最优收益:" + getBestGoldMining(w,g.length,p,g));
    }
}


说明:作者根据网络资料进行搜索学习,理解整理 若有侵权联系作者
参考:程序员小灰

猜你喜欢

转载自blog.csdn.net/qq_35655602/article/details/107826580