BM95 分糖果问题
知识点贪心
描述
一群孩子做游戏,现在请你根据游戏得分来发糖果,要求如下:
1. 每个孩子不管得分多少,起码分到一个糖果。
2. 任意两个相邻的孩子之间,得分较多的孩子必须拿多一些糖果。(若相同则无此限制)
给定一个数组 代表得分数组,请返回最少需要多少糖果。
要求: 时间复杂度为 空间复杂度为
数据范围: ,
示例1
输入:
[1,1,2]
复制返回值:
4
复制说明:
最优分配方案为1,1,2
示例2
输入:
[1,1,1]
复制返回值:
3
复制说明:
最优分配方案是1,1,1
题解
贪心解法
对于任意一个孩子i,他都需要和其左右进行比较,如果该孩子的分数高于左右之一,则当前孩子分得的最小糖果数应该是min(a[i-1],a[i+1]) + 1,否则就应该是1。貌似我们可以通过一次遍历对每个孩子比较其左右的值,但是由于左右的值又有依赖,因此无法通过空间复杂度为O(N)来解决该问题。假设我们用dp[i]数组来表示每个孩子应该分得的最小糖果数,那么对于任意的i,只要从左到右是递增的,则min(dp[i]) = dp[i-1] + 1,如果从右到左是递增的,则min(dp[i]) = dp[i+1] + 1。我们可以通过从左到右、从右到左2次遍历来解决该问题。
步骤:
- 使用数组dp[i]表示每个孩子分得的最小糖果数,初始化为1
- 从左到右遍历,如果a[i] > a[i-1],则dp[i] = dp[i-1]+1
- 从右往左遍历,如果a[i] > a[i+1] 且dp[i] <= dp[i+1],则dp[i] = dp[i+1]+1,因为如果不满足dp[i] <= dp[i+1]则说明孩子i获得的糖果数已经 大于其右边的孩子了
- 对dp进行累加,返回结果
代码如下:
// https://www.nowcoder.com/practice/76039109dd0b47e994c08d8319faa352?tpId=295&tags=&title=&difficulty=0&judgeStatus=0&rp=0&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3D%25E7%25AE%2597%25E6%25B3%2595%25E7%25AF%2587%26topicId%3D295
using namespace std;
int candy(const vector<int> &arr)
{
if (arr.size() <= 1)
{
return arr.size();
}
int res = 0;
std::vector<int> dp(arr.size(), 1);
for (int i = 1; i < arr.size(); ++i)
{
if (arr[i] > arr[i - 1])
{
dp[i] = dp[i - 1] + 1;
}
}
for (int i = arr.size() - 2; i >= 0; --i)
{
if (arr[i] > arr[i + 1] && dp[i] <= dp[i + 1])
{
dp[i] = dp[i + 1] + 1;
}
}
for (int i = 0; i < arr.size(); ++i)
{
res += dp[i];
}
return res;
}
使用坡度进行求解
以下解法来自于牛客网-原博客链接
解题思路:
- 想要用空间复杂度O(1)的解法,就需要不记录每个人分糖果的数量,而是在从左向右遍历的过程中寻找分配糖果的规律:根据孩子的得分来看,整个糖果分配的情况是一个折线图,且满足以下规律:
1.递增序列每次比前次加一。
2.递减序列的糖果数取决于递减序列长度与递增序列长度之间的大小关系:(1)若递减序列长度decLen小于递增序列长度incLen,则糖果数为1,2,3,……,decLen;(2)否则在此基础上还需要更新递增序列最后一个元素的值,从incLen更新为decLen。
3.每次遇到相同的得分,可视为清零重新开始。
- 以得分情况[1,2,3,4,3,2,2,1,4]为例,其糖果分配情况如下:
代码说明
用一个变量ans作为返回结果,表示最少需要分配的糖果总数;pre表示上一个人的糖果数,incLen表示递增序列长度,decLen表示递减序列长度。对于递增序列,只需要每次更新为pre+1,并将其加入ans中即可;对于递减序列,我们需要确定递减序列与递增序列之前的长度关系。
decLen++; ans+=decLen
是对递减序列1,2,3,……的累加,实际上的序列是……,3,2,1的累加,最终结果ans不变。
if(decLen==incLen) decLen++;
是对递减序列和递增序列之间的长度判断,在decLen<incLen的情况下,不需要考虑递增序列最后一位,因为右边的是小于它的,但是当decLen==incLen时,需要加该一位加入到递减序列中,因此decLen++。
代码:
class Solution {
public:
/**
* pick candy
* @param arr int整型vector the array
* @return int整型
*/
int candy(vector<int>& arr) {
// write code here
int n=arr.size(),ans=1;
//分别代表前一个孩子的糖果数,递增序列长度,递减序列长度。
int pre=1,incLen=1,decLen=0;
for(int i=1;i<n;i++){
if(arr[i]>=arr[i-1]){
//更新pre值,总糖果数ans,以及incLen和decLen
if(arr[i]==arr[i-1])
pre=1;
else
pre=pre+1;
ans+=pre;
incLen=pre;
decLen=0;
}
else{
//更新decLen,ans和pre
decLen++;
if(decLen==incLen)
decLen++;
ans+=decLen;
pre=1;
}
}
return ans;
}
};