【贪心算法】绿洲之旅:最少次数补给探索

文章目录

问题背景

假设一位旅行者需要穿越一片沙漠,起点到终点的距离为 D 公里,旅行者初始携带了 W 升水,每前进一公里需要消耗一升水。在穿越过程中,沿途会经过 N 个补给站,补给站的位置和可提供的水量分别由数组 positionsupply 描述。

旅行者的目标是用最少的补给次数到达终点。如果在某个时刻发现无法前行,则无法完成任务,输出 -1。

在这一问题中,我们需要兼顾两个核心点:

  1. 每次补给的时机:选择适当的时机进行补给,以保证旅程能够继续。
  2. 补给的优先级:从多个补给选项中,选择能让旅程覆盖最远距离的补给量。

解决思路

从问题描述来看,它具备明显的贪心特性:我们需要在每个关键点(当前位置无水前行时)做出最优决策,尽量选择当前能提供最多水量的补给站,以减少未来补给次数。具体的解决思路如下:

  1. 按距离排序补给站
    补给站的位置不一定是按顺序给出的,因此需要先将它们按距离从小到大排序。这样可以保证旅行者在沿途遇到补给站的顺序与实际前进顺序一致。
  2. 维护一个最大堆记录可选补给量
    在每经过一个补给站时,将其可提供的水量存入最大堆中。当发现当前携带的水量不足以到达下一个目的地时,从堆中取出最大值进行补给。这种策略确保了当前的补给选择是对后续行程最有利的。
  3. 处理终点作为特殊情况
    在所有补给站处理完后,还需要考虑终点的情况。如果当前水量仍不足以到达终点,则需要继续从最大堆中补给,否则任务失败。
  4. 计算补给次数
    每次从堆中取水,即为一次补给。通过计数器记录补给次数,最终返回结果。

贪心算法的优势

这一问题选择贪心算法有以下几个优势:

  1. 局部最优解的有效性
    在每次面临"是否需要补给"的选择时,选用当前能提供最大补给量的站点,总能为未来节省更多水量需求,减少补给次数。
  2. 时间效率高
    贪心算法主要依赖排序和堆操作,时间复杂度为 O(nlog⁡n),非常适合处理大规模输入。
  3. 简单易实现
    贪心算法直接针对问题的局部最优策略构建解决方案,逻辑清晰,代码量少。

实现步骤详解

具体的实现可以分为以下几个步骤:

  1. 输入数据的预处理

    将补给站信息存储为一个二维数组,并按照距离进行排序。

  2. 使用最大堆记录补给量

    遍历每个补给站时,将其补给量加入最大堆。在水量不足时,从堆中提取最大补给量。

  3. 终点检查

    当所有补给站处理完后,仍需检查剩余水量是否能覆盖到终点。如果无法到达,则返回 -1。

  4. 补给次数统计

    每次补充水量,计数器加一,最后输出最少补给次数。

java 复制代码
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;

public class travel_lvzhou {

        public static int solution(int d, int w, int[] position, int[] supply) {
            int n = position.length;
            // 补给站按距离排序
            int[][] stations = new int[n][2];
            for (int i = 0; i < n; i++) {
                stations[i][0] = position[i];
                stations[i][1] = supply[i];
            }
            Arrays.sort(stations, Comparator.comparingInt(a -> a[0]));

            // 最大堆存储可以选择的补给量
            PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
            int currentWater = w; // 当前水量
            int refills = 0; // 补给次数
            int prevPosition = 0; // 上一个位置

            for (int i = 0; i <= n; i++) {
                // 当前目标点,补给站或终点
                int currentPosition = (i < n) ? stations[i][0] : d;
                int distance = currentPosition - prevPosition;

                // 如果当前水量不足以到达下一个站点,进行补给
                while (currentWater < distance) {
                    if (maxHeap.isEmpty()) {
                        return -1; // 无法到达
                    }
                    currentWater += maxHeap.poll(); // 从最大堆中取最大水量
                    refills++;
                }

                currentWater -= distance;
                prevPosition = currentPosition;

                // 如果是补给站,将其水量加入堆中
                if (i < n) {
                    maxHeap.offer(stations[i][1]);
                }
            }

            return refills;
        }

        public static void main(String[] args) {
            // You can add more test cases here
            int[] testPosition = {170, 192, 196, 234, 261, 269, 291, 404, 1055, 1121, 1150, 1234, 1268, 1402, 1725, 1726, 1727, 1762, 1901, 1970};
            int[] testSupply = {443, 185, 363, 392, 409, 358, 297, 70, 189, 106, 380, 130, 126, 411, 63, 186, 36, 347, 339, 50};

            System.out.println(solution(10, 4, new int[]{1, 4, 7}, new int[]{6, 3, 5}) == 1);
            System.out.println(solution(2000, 200, testPosition, testSupply) == 5);
        }

}

外链: 绿洲之旅:最少次数补给探索

相关推荐
Haooog13 分钟前
替换数字(字符串算法)
算法
hansang_IR13 分钟前
【算法速成课1 | 题解】洛谷P3366 【模板】最小生成树 MST(Prim & Kruskal)
c++·笔记·算法·题解·最小生成树·kruskal·prim
一起努力啊~15 分钟前
算法题打卡力扣第15题:三数之和(mid)
算法·leetcode·职场和发展
快去睡觉~18 分钟前
力扣18:四数之和
算法·leetcode·深度优先
超级大坏蛋20182 小时前
QT .pro文件的常见用法
java·linux·qt
祈祷苍天赐我java之术8 小时前
Linux 进阶之性能调优,文件管理,网络安全
java·linux·运维
无处不在的海贼9 小时前
小明的Java面试奇遇之发票系统相关深度实战挑战
java·经验分享·面试
武子康9 小时前
Java-109 深入浅出 MySQL MHA主从故障切换机制详解 高可用终极方案
java·数据库·后端·mysql·性能优化·架构·系统架构
秋难降9 小时前
代码界的 “建筑师”:建造者模式,让复杂对象构建井然有序
java·后端·设计模式
mit6.8249 小时前
8.27 网格memo
c++·算法