一、问题理解
题目要求我们在每天都要消耗一份食物的情况下,找到最后购买价格总数最低的方案。题目中比较需要注意的点是:
- 通过计算一些简单的测试案例,可以知道买与吃的顺序是“先买后吃”。假设我最多可携带3份食物,而我当天(还没吃的情况下)有1份食物,那么我当天只能再购买2份,不可以先把当天的食物吃掉,再购买3份。
- 基于“先买后吃”的规则,我们还可以知道题目允许存在还未购买食物的情况下当天食物为0的情况。假设我今天还有2份食物,则我可以在今天(第一天)、第二天消耗掉的情况下,撑到第三天再进行购买。
二、方法和变量介绍
主函数:
- solution
辅助方法:
- findMin: 在给定的开始时间和结束时间中,找到最小值的索引并返回。边界值包含在内。
- findSmaller: 在给定的开始日期和结束日期中,返回找到的第一个更低价日期。若没有更低价,返回开始日期。边界值包含在内。
变量:
- carryMax:最大携带量
- n:总天数
- data:数组,表示每天的食物价格
- day:当天日期(从0开始,第一天day=0)
- food:当天的食物数量
- price:采购食物的价格总数
- supply:购入食物的日子
- nextBegin:比供应日更便宜的日子
- buyAmount:购入食物的数量
三、代码思路说明
- 首先调用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);
- 在找到供应日后,我们需要决定供应日当天要购入的数量。供应日为开始,我们最多可以撑到carryMax天后,调用findSmaller查找这段时间内有没有比供应日价格更低的。要注意这个地方需要比较一下结束日(供应日+carryMax)和总天数(-1是因为索引从0开始),避免访问非法数组的问题。
int nextBegin = findSmaller(data, supply, Math.min(supply + Carrying_max, n - 1));
- 接下来分为两种情况,一是找到了比供应日价格更低的(nextBegin > supply)。则我们只要能刚好撑到更低价的那天(nextBegin),补充的数量为nextBegin - supply - food。记得把价格总数、天数和食物更新到nextBegin当天的情况。
- 第二种情况是没有找到比供应日当天价格更低的。首先比较剩下的天数和最大携带量。要是最大携带量比较小,就把食物补充到最大携带量,补充的数量为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);
- 除了第二种情况下,我们已经补充到了足够支撑到最后一天的数量,不需要再更新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;
}
}