zoukankan      html  css  js  c++  java
  • 挖金矿

    一、问题描述

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

    二、算法分析

    w 表示总共人数,n 表示金矿数,

    1. 排列组合

      每一座金矿都有挖与不挖两种选择,排列组合起来就有 2n 种选择。对所有可能性做遍历,剔除那些使用工人数超过 10 的选择,在剩下的选择里找出获得金币数最多的选择。

      时间复杂度:O(2n)。

    2. 递归

      每个金矿存在"挖"、"不挖"两种情况,可以同时内部调用两次递归方法,表示"挖"和"不挖"。

      void A()
      {
      if (边界条件) return x;
      // 挖
      A();
      // 不挖
      A();
      }

      时间复杂度:O(2n) 开辟空间:递归深度 O(n)

    3. 动态规划

      这个问题与 0-1 背包问题相同,动态规划时的策略也是:当前金矿是否应该挖,挖与不挖的价值比较。整理出以下表格。


      【状态】是 f(w),【边界】是 f(w < 3) = 0;状态方程 f(w) = max{ f(w - 1), f(w - i) + vi }, w > i

      i 表示当前金矿需要的人数,vi 表示当前金矿的价值。

      时间复杂度:O(n*w)

      空间复杂度:O(n*w)


      如果不保留挖金矿信息,只输出最高金币数,可以由上发现,每一层的数据只与上一层有关,那么就可以由上至下,只用一位数组保存状态,空间复杂度 O(w)

    4. 比较

      当工人数 w -> ∞ 时,动态规划的时间复杂度和空间复杂度与 w 成正比,所以增长较快;而递归算法与 w 无关,所以不增长,此时动态规划的效率就没有递归的好。

    三、代码实现

    ①、递归(自底向上)

    #include <stdio.h>
    #include <stdlib.h>
    
    #define MAX(a, b)  (a) > (b) ? (a) : (b)
    
    typedef struct GoldMine {
        int worker;
        int gold;
    } GoldMine;
    
    /**
     *  mineNum   金矿数
     *  workerNum 挖矿总人数
     *  curMine   当前金矿(从 0 开始)
     *  curWorker 当前人数(从 0 开始)
     *  max       当前最大金币数
     */        
    int monerFinderAlgorithm(GoldMine* mines, int mineNum, int workerNum, int curMine, int curWorker, int max)
    {
        GoldMine curGoldMine = mines[curMine];
        // 金矿挖完了 || 人数不够了
        if (curMine >= mineNum || curWorker + curGoldMine.worker > workerNum) return max;
        
        // 挖
        int dig = monerFinderAlgorithm(mines, mineNum, workerNum, curMine + 1, curWorker + curGoldMine.worker, max + curGoldMine.gold);
        
        // 不挖
        int noDig = monerFinderAlgorithm(mines, mineNum, workerNum, curMine + 1, curWorker, max);
        
        return MAX(dig, noDig);
    }
    
    int main()
    {
        GoldMine mines[5] = { {3, 200}, {4, 300}, {3, 350}, {5, 400}, {5, 500} };
        printf("
    
    %d", monerFinderAlgorithm(mines, 5, 10, 0, 0, 0));
        
        //GoldMine mines[2] = { {4, 300}, {5, 500} };
        //printf("
    
    %d", monerFinderAlgorithm(mines, 2, 10, 0, 0, 0));
        
        return 0;
    }
    

    ②、动态规划

    #include <stdio.h>
    #include <stdlib.h>
    
    #define MAX(a, b)   (a) > (b) ? (a) : (b)
    
    typedef struct GoldMine {
        int worker;
        int gold;
    } GoldMine;
    
    /// mineNum 金矿数     worker 工人数
    int monerFinderAlgorithm(GoldMine* mines, int mineNum, int worker)
    {
        if (worker == 0 || mineNum == 0)  return 0;
        
        // i 列    j 行
        int i = 0, j = 0;
        
        // 二维数组,从左向右,第一个 [ ] 是列,第二个 [ ] 是行
        int goldMatrix[worker + 1][mineNum];
        // 初始化并打印二维数组
        for (; j < mineNum; j++) {
            for (i = 0; i <= worker; i++) {
                goldMatrix[i][j] = 0;
                printf("0    ");  // printf("%d    ", goldMatrix[i][j]);
            }
            printf("
    ");
        }
        printf("
    ");
        
        GoldMine mine;
        
        for (i = 1; i <= worker; i++) {
            for (j = 0; j < mineNum; j++) {
                
                mine = mines[j];
                
                // 挖矿人数不够
                if (mine.worker > i) {
                    // 第一个存储 0,非第一个存储前一个 j 的值
                    goldMatrix[i][j] = (j == 0) ? 0 : goldMatrix[i][j-1];
                }
                // 挖矿人数足够
                else {
                    // 第一个直接存储,非第一个存储 MAX{  不加入 j 的值, 加入 j 的值  },j -1 是因为剔除了当前
                    goldMatrix[i][j] = (j == 0) ? mine.gold : MAX(goldMatrix[i][j-1], goldMatrix[i-mine.worker][j-1] + mine.gold);
                }
            }
        }
        
        
        // 打印二维数组内容
        for (j = 0; j < mineNum; j++) {
            for (i = 1; i <= worker; i++)
                printf("%d    ", goldMatrix[i][j]);
            printf("
    ");
        }
        printf("
    ");
        
        // 挖哪些矿
        int curWorker = worker;
        
        for (j = mineNum - 1; j >= 0; j--) {
            
            mine = mines[j];
            
            if (curWorker == 0) {
                break;
            }
            
            // 根据变换公式从上至下获得物品
            if (goldMatrix[curWorker][j] - goldMatrix[curWorker - mine.worker][j-1] == mine.gold) {
                printf("%d   ", mine.worker);
                curWorker -= mine.worker;
            }
        }
        
        return goldMatrix[worker][mineNum - 1];
    }
    
    int main()
    {
        GoldMine mines[5] = { {3, 200}, {4, 300}, {3, 350}, {5, 400}, {5, 500} };
        printf("
    
    %d", monerFinderAlgorithm(mines, 5, 10));
        
        //GoldMine mines[2] = { {4, 300}, {5, 500} };
        //printf("
    
    %d", monerFinderAlgorithm(mines, 2, 10));
        
        return 0;
    }
    
    
    0    0    0    0    0    0    0    0    0    0    0    
    0    0    0    0    0    0    0    0    0    0    0    
    0    0    0    0    0    0    0    0    0    0    0    
    0    0    0    0    0    0    0    0    0    0    0    
    0    0    0    0    0    0    0    0    0    0    0    
    0    0    200    200    200    200    200    200    200    200    
    0    0    200    300    300    300    500    500    500    500    
    0    0    350    350    350    550    650    650    650    850    
    0    0    350    350    400    550    650    750    750    850    
    0    0    350    350    500    550    650    850    850    900    
    5   5
       
    900
    

    空间复杂度 O(n)

    /// mineNum 金矿数     worker 工人数
    int monerFinderAlgorithm(GoldMine* mines, int mineNum, int worker)
    {
        if (worker == 0 || mineNum == 0)  return 0;
        int* preGoldMatrix = (int *)calloc(sizeof(int), worker);
        int* goldMatrix    = (int *)calloc(sizeof(int), worker);
        GoldMine mine;
        for (int i = 0; i < mineNum; i++) {
            mine = mines[i];
            // 从 1 开始计算
            for (int j = 1; j <= worker; j++) {
                // 挖矿人数不够
                if (j < mine.worker) {
                    goldMatrix[j-1] = preGoldMatrix[j-1];
                }
                // 挖矿人数足够
                else {
                    // 从上一层中 [j - mine.worker - 1] 和 [j - 1] 中获取最大值
                    goldMatrix[j-1] = MAX(preGoldMatrix[j-1], preGoldMatrix[j - mine.worker - 1] + mine.gold);
                }
                
                printf("%d   ", goldMatrix[j - 1]);
            }
            printf("
    ");
            
            // 打印 preGoldMatrix、goldMatrix 数组的内容
    //        for (int k = 0; k < worker; k++) {
    //            printf("%d    ", preGoldMatrix[k]);
    //        }
    //        printf("
    ");
    //
    //        for (int k = 0; k < worker; k++) {
    //            printf("%d    ", goldMatrix[k]);
    //        }
    //        printf("
    
    ");
            
            for (int k = 0; k < worker; k++) {
                // 不能使用 preGoldMatrix = goldMatrix;  这是指针赋值,preGoldMatrix 与 goldMatrix 内存地址一样
                preGoldMatrix[k] = goldMatrix[k];
            }
            
            //printf("%p   %p", preGoldMatrix, goldMatrix);  // 打印数组的内存地址
        }
        return goldMatrix[worker - 1];
    }
    

    四、内容来源

    漫画:什么是动态规划?

  • 相关阅读:
    C#无限极分类树-创建-排序-读取 用Asp.Net Core+EF实现之方法二:加入缓存机制
    如何将CKeditor编辑器的上传和thinkphp结合
    在 VisualStudio 给文件起一个带分号的文件名会怎样
    dotnet ConditionalWeakTable 的底层原理
    GitHub 的 Action 判断仅在主仓库才执行脚本
    ASP.NET Core 将文件夹内容输出为压缩包文件方法
    dotnet Microsoft.Recognizers.Text 超强大的自然语言关键词提取库
    dotnet CBB 为什么决定推送 Tag 才能打包
    WPF 通过 InputManager 模拟调度触摸事件
    如何参与 .NET 的开发和设计
  • 原文地址:https://www.cnblogs.com/dins/p/dig-gold.html
Copyright © 2011-2022 走看看