最近在学习rust语言,用刷题来熟悉基础用法,遇见了这个徒步旅行的题;
题目如下:
这道题在网上有很多解法,其中我个人感觉比较特别的一种思路,我用rust语言来实现,思路如下:
fn solution(n: i32, k: i32, data: Vec<i32>) -> i32 {
let mut min_arr: Vec<[i32; 2]> = Vec::new();
let mut i = 0;
let mut sum = 0;
while i < n {
while !min_arr.is_empty() && min_arr.last().unwrap()[1] > data[i as usize] {
min_arr.pop();
}
min_arr.push([i, data[i as usize]]);
while min_arr[0][0] <= i - k {
min_arr.remove(0);
}
println!("e-----{:?}", min_arr);
sum += min_arr[0][1];
i += 1;
}
println!("{}", sum);
sum
}
结果:
e-----[[0, 1]]
e-----[[0, 1], [1, 2]]
e-----[[1, 2], [2, 3]]
e-----[[2, 3], [3, 3]]
e-----[[4, 2]]
9
true
e-----[[0, 4]]
e-----[[1, 1]]
e-----[[1, 1], [2, 5]]
e-----[[1, 1], [3, 2]]
e-----[[4, 1]]
e-----[[4, 1], [5, 3]]
9
true
e-----[[0, 3]]
e-----[[1, 2]]
e-----[[2, 4]]
e-----[[3, 1]]
10
true
这个思路是将数组中的元素与索引一起保存在min_arr中,然后每次取出最小的元素,并判断是否超出滑动窗口的范围。相当于每天的消耗都是从过去时间或当前取,一种逆向思维。
针对题中样例一的问题
题中样例一是
输入:n = 5 ,k = 2 ,data = [1, 2, 3, 3, 2]
输出:9
按照手动一天一天的排的结果总是8;思路如下:
第一天:先买2 吃1 剩1
第二天:先吃1 买2 剩2
第三天:吃1 买0 剩1
第四天:吃1 买0 剩0
第五天:先买1 吃1 剩0
总的加个是1+1+2+2+2=8,和题中答案不一致,和上面思路算出来也不一样。
思考:
按照题目给出的解法和上面那种思路,对于样例一是1+1+2+3+2=9,和上面手动推导的区别在于第二天,题目解法是先买了后吃,第二天只能购买1份;我手动推导是先吃后买,第二天可以购买2份。
个人感觉手动推导的更符合实际点,所以想将上面那种思路改造一下,试了好多种方法都改不了,问题点在那也没想通,希望有大佬帮忙解释一下,所以我按推导的思路重新实现了一下。
新思路
fn solution_new(n: i32, k: i32, data: Vec<i32>) -> i32 {
let mut sum = 0;
let mut food_num = 0;
for i in 0..n as usize {
let mut is_eat = false;
// 有食物时 先消耗 并记录是否吃了
if food_num > 0 {
food_num -= 1;
is_eat = true;
}
// 初始化每天购买量,先吃了就为0,没吃就为 1
let mut buy_food_num = if is_eat { 0 } else { 1 };
// 按照最大携带量k,计算最大能坚持到第几天,当天已经吃了就多加一天
let end: i32 = if is_eat {
i as i32 + k + 1
} else {
i as i32 + k
};
// 最大天数不能超过n天;
let max = if end > n { n } else { end };
let mut last_food = food_num;
// 循环后面的天数,如果后面天的价格大于当天,就买一份;否则接终止循环,等到了这天再买
for j in (i as i32 + 1) as usize..max as usize {
if data[j] >= data[i] {
// 后续天数先消耗之前的剩余食物,消耗完再购买
if last_food > 0 {
last_food -= 1;
} else {
buy_food_num += 1;
}
} else {
break;
}
}
println!("buy {}", buy_food_num);
sum += data[i] * buy_food_num;
food_num += buy_food_num;
if !is_eat {
food_num -= 1;
}
}
println!("{}", sum);
sum
}
结果:
buy 2
buy 2
buy 0
buy 0
buy 1
8
true
buy 1
buy 3
buy 0
buy 0
buy 2
buy 0
9
true
buy 1
buy 1
buy 1
buy 1
10
true
两种思路的测试如下:
pub fn test() {
// 1+1+2+3+2=9
println!("{}", solution(5, 2, vec![1, 2, 3, 3, 2]) == 9);
// 4+1+1+1+1+1=9
println!("{}", solution(6, 3, vec![4, 1, 5, 2, 1, 3]) == 9);
// 3+2+4+1=10
println!("{}", solution(4, 1, vec![3, 2, 4, 1]) == 10);
print!("------------------------------------------\n");
// 1+1+2+2+2=8 先消耗后购买 2 2 0 0 1
println!("{}", solution_new(5, 2, vec![1, 2, 3, 3, 2]) == 8);
// 4+1+1+1+1+1=9
println!("{}", solution_new(6, 3, vec![4, 1, 5, 2, 1, 3]) == 9);
// 3+2+4+1=10
println!("{}", solution_new(4, 1, vec![3, 2, 4, 1]) == 10);
}
补充
查找规律发现,每天的价格符合[a,...([k-2]个比a,b大的数),b,...(k个比b大的数),c] 这样的都会出现上述先吃后买的问题
有问题,请指正哦