问题描述
在猫星球上,小R负责给一行排队的猫分发鱼干。每只猫有一个等级,等级越高的猫应该得到更多的鱼干。规则如下:
- 每只猫至少得到一斤鱼干。
- 如果一只猫的等级高于它相邻的猫,它就应该得到比相邻的猫更多的鱼干。
小R想知道,为了公平地满足所有猫的等级差异,他至少需要准备多少斤鱼干。
测试样例
样例1:
输入:
n = 3, cats_levels = [1, 2, 2]
输出:4
样例2:
输入:
n = 6, cats_levels = [6, 5, 4, 3, 2, 16]
输出:17
样例3:
输入:
n = 20, cats_levels = [1, 2, 2, 3, 3, 20, 1, 2, 3, 3, 2, 1, 5, 6, 6, 5, 5, 7, 7, 4]
输出:35
思路分析
首先分析题目,得到以下重要信息:
- 无需关心猫的等级的具体数值,只关心相邻猫的等级的大小关系即可
- 如果一只猫的等级比相邻猫的等级大/小,它得到的鱼干比相邻猫多/少;如果一只猫的等级与相邻猫的等级相等,它们的鱼干数互相没有影响
下面我们逐层递进地分析:
假设 catsLevels 数组是严格单调递增的,那么第一只猫分配一个鱼干,然后依次递增即可。例如 catsLevels = {0,1,2,3,4,5},分配的鱼干数就是 {1,2,3,4,5,6}。
假设 catsLevels 数组是单调非递减的,那么当遇到两只猫的等级相等时,后一只猫分配一个鱼干,然后再依次递增。例如 catsLevels = {0,1,2,2,3,4,4,5},分配的鱼干数就是 {1,2,3,1,2,3,1,2}。
而实际上, catsLevels 数组不一定是单调的,当出现了一段递减的序列,由于是从前往后遍历的,在找到极小值之前无法对前面的数据赋值(如果从后往前遍历,那么在遇到递增序列时会遇到问题)。为了处理这个问题,可以记录每一个递减序列的开始位置,在递减序列结束后进行处理。同时,为了减少代码量,避免分类讨论,直接把单个元素也看作是一个递减序列。
代码实现
#include <iostream>
#include <vector>
#include <algorithm>
int solution(int n, std::vector<int> catsLevels) {
//在数组末尾添加“哨兵”,处理边界问题
catsLevels.push_back(catsLevels[n-1]);
//遍历数组,计算每只猫的最小鱼干数
int top = 0;
std::vector<int> fish(n, 1);
for(int i = 1; i <= n; i++) {
if(catsLevels[i] >= catsLevels[i-1]) {
fish[top] = std::max(fish[top], i - top);
for(int j = top + 1; j < i; j++)
fish[j] = i - j;
if(catsLevels[i] > catsLevels[i-1]) fish[i] = fish[i-1] + 1;
top = i;
}
}
//计算总的鱼干数
int count = 0;
for(int i = 0; i < n; i++)
count += fish[i];
return count;
}
代码的实现就是遍历数组,如果一只猫的等级不小于前一只猫的等级,就是开启了一个新的递减序列,对上一个序列进行处理。序列中最后一只猫分配一个鱼干,向前依次递增,而如果序列的第一只猫的等级大于前面的一只猫,其分配的鱼干数要比前一只猫多 1 ,并且与序列中得到的值取最大值。
代码开头先在 catsLevels 末尾添加了一个哨兵,当遍历到哨兵时会处理最后一个递减序列,简化边界条件的处理
复杂度分析
时间复杂度