徒步旅行中的补给问题 | 豆包MarsCode AI刷题

122 阅读4分钟

一、问题理解

题目要求我们在每天都要消耗一份食物的情况下,找到最后购买价格总数最低的方案。题目中比较需要注意的点是:

  1. 通过计算一些简单的测试案例,可以知道买与吃的顺序是“先买后吃”。假设我最多可携带3份食物,而我当天(还没吃的情况下)有1份食物,那么我当天只能再购买2份,不可以先把当天的食物吃掉,再购买3份。
  2. 基于“先买后吃”的规则,我们还可以知道题目允许存在还未购买食物的情况下当天食物为0的情况。假设我今天还有2份食物,则我可以在今天(第一天)、第二天消耗掉的情况下,撑到第三天再进行购买。

二、方法和变量介绍

主函数:

  • solution

辅助方法:

  • findMin: 在给定的开始时间和结束时间中,找到最小值的索引并返回。边界值包含在内。
  • findSmaller: 在给定的开始日期和结束日期中,返回找到的第一个更低价日期。若没有更低价,返回开始日期。边界值包含在内。

变量:

  • carryMax:最大携带量
  • n:总天数
  • data:数组,表示每天的食物价格
  • day:当天日期(从0开始,第一天day=0)
  • food:当天的食物数量
  • price:采购食物的价格总数
  • supply:购入食物的日子
  • nextBegin:比供应日更便宜的日子
  • buyAmount:购入食物的数量

三、代码思路说明

  1. 首先调用findMin找出供应日:设当天为开始,食物消耗完的最后一天为结束,在这段时间中我们必须找出一个价格最低的日子作为供应日(supply)。找出供应日后,我们将数据(当天日期day与食物food)更新到supply当天,并打印供应日这天的数据以便后期测试与调整(打印的代码不注释也不影响ai刷题中代码的提交)。
int supply = findMin(data, day, Math.min(day + food, n - 1));
food -= (supply - day); // 模拟食物消耗
day = supply;
System.out.println("day:" + supply + " food:" + food);
  1. 在找到供应日后,我们需要决定供应日当天要购入的数量。供应日为开始,我们最多可以撑到carryMax天后,调用findSmaller查找这段时间内有没有比供应日价格更低的。要注意这个地方需要比较一下结束日(供应日+carryMax)和总天数(-1是因为索引从0开始),避免访问非法数组的问题。
int nextBegin = findSmaller(data, supply, Math.min(supply + Carrying_max, n - 1));
  1. 接下来分为两种情况,一是找到了比供应日价格更低的(nextBegin > supply)。则我们只要能刚好撑到更低价的那天(nextBegin),补充的数量为nextBegin - supply - food。记得把价格总数、天数和食物更新到nextBegin当天的情况。
  2. 第二种情况是没有找到比供应日当天价格更低的。首先比较剩下的天数和最大携带量。要是最大携带量比较小,就把食物补充到最大携带量,补充的数量为Carrying_max - food,然后要更新价格总数和食物数量,正常进入下一天;要是剩下的天数比较小,则补充剩下天数的量就够了。在每次循环中打印出价格总数,方便我们观察可能出现的错误。
if (nextBegin > supply) {
        int buyAmount = nextBegin - supply - food;
        price += buyAmount * data[supply];
        food = 0;
        day = nextBegin;
} else {
        if(Carrying_max < n - food){
                price += (Carrying_max - food) * data[supply];
                food = Carrying_max - 1;//补充到最大携带量后当天消耗一份食物
                day = supply + 1;
         }else{
                price += (n - supply - food) * data[supply];
                break;
         }
}
System.out.println("price:" + price);
  1. 除了第二种情况下,我们已经补充到了足够支撑到最后一天的数量,不需要再更新day。其他的时候,都有更新day,在day不大于n的情况下,循环则不断执行。

四、整段完整代码

public class Main {
    public static int solution(int n, int Carrying_max, int[] data) {
        int food = 0;
        int price = 0;

        for (int day = 0; day < n;) {
            // 找出供应日:在食物消耗完的天数内选择最便宜的一天
            int supply = findMin(data, day, Math.min(day + food, n - 1));
            food -= (supply - day);
            day = supply;
            System.out.println("day:" + supply + " food:" + food);

            // 查找是否有比供应日价格更低的
            int nextBegin = findSmaller(data, supply, Math.min(supply + Carrying_max, n - 1));

            if (nextBegin > supply) {
                // 有更低价格天数,购买足够撑到那天的食物
                int buyAmount = nextBegin - supply - food;
                price += buyAmount * data[supply];
                food = 0;
                day = nextBegin;
            } else {
                // 没有找到比该天更便宜的
                if(Carrying_max < n - supply){
                    price += (Carrying_max - food) * data[supply];
                    food = Carrying_max - 1;
                    //正常进入下一天
                    day = supply + 1;
                }
                else
                {
                    price += (n - supply - food) * data[supply];
                    break;
                }
            }
            System.out.println("price:" + price);
        }
        return price;
    }

    public static int findMin(int[] arr, int begin, int end) {
        int minIndex = begin;
        for (int i = begin + 1; i <= end; i++) {
            if (arr[i] < arr[minIndex]) {
                minIndex = i;
            }
        }
        return minIndex;
    }

    public static int findSmaller(int[] arr, int begin, int end) {
        for (int i = begin + 1; i <= end; i++) {
            if (arr[i] < arr[begin]) {
                return i;
            }
        }
        return begin;
    }
}