贪心算法 - 多机调度问题 & 钱币找零 & 跳跃游戏

发布于:2022-11-28 ⋅ 阅读:(375) ⋅ 点赞:(0)

目录

1.多机调度问题

1.1 题目描述

1.2 解题思路

1.3 代码实现

2. 钱币找零

2.1 题目描述

2.2 解题思路

2.3 代码实现

3. 跳跃游戏

3.1 题目描述

3.2 解题思路

3.3 代码实现


1.多机调度问题

1.1 题目描述

某工厂有n个独立的作业,由m台相同的机器进行加工处理。作业i所需的加工时间为ti,
任何作业在被处理时不能中断,也不能进行拆分处理。现厂长请你给他写一个程序:
算出n个作业由m台机器加工处理的最短时间

输入
第一行T(1<T<100)表示有T组测试数据。每组测试数据的第一行分别是整数n,m
(1<=n<=10000,1<=m<=100),接下来的一行是n个整数ti(1<=t<=100)。
输出
所需的最短时间

1.2 解题思路

多机调度问题用贪心策略来解决的时候, 就可以分两种情况来讨论:

1. 机器数 > 作业数

  • 那么每个机器只需要完成一个作业即可, 那么机器处理的最短时间, 就是装有最大作业的完成时间, 就只需要将作业排好序, 返回最大的那一个作业即可.

       

2. 机器数 < 作业数

  • 每次寻找装有最小作业的机器, 然后将任务从大到小取出来挂在装有最小作业的机器上, 当作业全部挂在机器上的时候, 只需返回机器中装有的作业的最大值即可. 

1.3 代码实现

    public static int getMatTime(int[] taskTime, int[] machines) {
        Arrays.sort(taskTime);
        int n = taskTime.length;
        int m = machines.length;
        // 作业数 < 机器数
        if(n <= m) {
            return taskTime[n - 1];
        } else {
            for(int i = n - 1; i >= 0; i--) {
                int minMachinesTime = machines[0];
                int finish = 0;
                // 寻找一个装最小作业的机器
                for(int j = 1; j < m; j++) {
                    if(machines[j] < minMachinesTime) {
                        // 记录该机器下标
                        finish = j;
                        minMachinesTime = machines[j];
                    }
                }
                // 将最大作业挂在装有最小作业的机器上
                machines[finish] += taskTime[i];
            }
        }
        // 返回装有最大作业的机器中的作业数
        return findMax(machines);
    }
    public static int findMax(int[] machines) {
        int max = machines[0];
        for(int j = 1; j < machines.length; j++) {
            if(machines[j] > max) {
                max = machines[j];
            }
        }
        return max;
    }
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int taskNum = scan.nextInt();
        int machineNum = scan.nextInt();

        int[] taskTime = new int[taskNum];
        int[] machines = new int[machineNum];
        for(int i = 0; i < taskNum; i++) {
            taskTime[i] = scan.nextInt();
        }
        System.out.println(getMatTime(taskTime, machines));
    }

2. 钱币找零

2.1 题目描述

假设1元、2元、5元、10元、20元、50元、100元的纸币分别有c0, c1, c2, c3, c4, c5, c6张。
现在要用这些钱来支付K元,至少要用多少张纸币?

2.2 解题思路

要支付 k 元, 我们只需要不断选择面值大的纸币来支付就行了, 如果所有纸币都支付完了, k 还大于 0 , 那么就说明支付不起, 如果 k == 0, 就返回使用的纸币张数, 且这种使用方法,使用的纸币张数是最少的. (将一个表示纸币面值和张数的二维数组基于纸币面值排序)

2.3 代码实现

    public static int solve(int money, int[][] moneyCount) {
        Arrays.sort(moneyCount, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o2[0] - o1[0];
            }
        });
        int count = 0;
        int maxMoneyNumber = 0;
        for(int i = moneyCount.length - 1; i >= 0; i--) {
            // 需要的当前面值与面值数量取最小
            maxMoneyNumber = Math.min(money / moneyCount[i][0], moneyCount[i][1]);
            money = money - maxMoneyNumber * moneyCount[i][0];
            count += maxMoneyNumber;
        }
        // 如果待支付的钱还大于 0, 则支付不起
        if(money > 0) {
            return -1;
        } else {
            return count;
        }
    }
    public static void main(String[] args) {
        int[][] moneyCount = { { 100, 10 }, { 2, 1 }, { 10, 3 }, { 5, 4 }, { 20, 0 } ,{50, 1}, { 1, 3 }};
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入要支付的钱: ");
        int money = scan.nextInt();

        int res = solve(money, moneyCount);
        if(res == -1) {
            System.out.println("余额不足!");
        } else {
            System.out.println(res);
        }
    }

3. 跳跃游戏

3.1 题目描述

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

示例 1

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2

输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 
所以永远不可能到达最后一个下标。

3.2 解题思路

设想一下,对于数组中的任意一个位置 y,我们如何判断它是否可以到达?
根据题目的描述, 只要存在一个位置 x, 它本身可以到达最后一个下标,并且它跳跃的最大长度为 x + nums[x], 这个值大于等于 y, 即 x+nums[x] ≥ y, 那么位置 y 也可以到达最后一个下标.  也就是说, 对于任意一个可以到达最后下标的位置 x, 它可以使得 x + 1, x + 2 .... x + nums[x] 这些连续的位置都可以到达. 
  • 不断遍历数组中每一个位置, 并实时维护最远可以到达的.在跳跃的过程中可以通过         x + num[x] 不断更新跳跃距离的最大值.
  • 在遍历的过程中, 如果最远可以到达的位置大于等于数组中的最后一个位置,那就说明最后一个位置可达,直接返回 true, 如果在遍历结束后,跳不到最后一个位置,就返回 false.

3.3 代码实现

    public boolean canJump(int[] nums) {
        if(nums.length == 0 || nums == null) {
            return true;
        }
        int max = 0;
        for(int i = 0; i < nums.length; i++) {
            // 是否可以到达当前位置
            if(max >= i) {
                max = Math.max(max, i + nums[i]);
                // 到达的最远的位置是否包含最后一个位置
                if(max >= nums.length - 1) {
                    return true;
                }
            } else {
                // 达到不了,直接返回 false
                return false;
            }
        }
        return true;
    }

本文含有隐藏内容,请 开通VIP 后查看