糖果传递游戏
问题描述
小U和 n 个小朋友正在玩一个有趣的糖果传递游戏。n 个小朋友按顺序排成一列,小U站在最右边的位置。游戏开始时,小U为每个小朋友分配了 a[1], a[2], ..., a[n] 个糖果。现在,小U手里还剩下 m 个糖果要分配。
分配规则如下:
- 每个糖果都会先从第一个小朋友开始分配。如果当前小朋友的糖果数量(包括新分配的糖果)小于或等于他的右边的小朋友,则他会保留这个糖果。
- 如果当前小朋友的糖果数量大于他右边的小朋友,他会将这个糖果传递给下一个小朋友。
- 这个过程会持续,直到糖果传递到第
n个小朋友。如果第n个小朋友也不能保留这个糖果,那么糖果会被传递给小U。
现在,请你帮忙确定第 m 个糖果最终会分配给哪个小朋友,或者传递给小U。
测试样例
样例1:
输入:
n = 4, m = 3, a = [1, 2, 3, 4]
输出:1
样例2:
输入:
n = 4, m = 2, a = [4, 3, 2, 3]
输出:5
样例3:
输入:
n = 4, m = 2, a = [4, 3, 2, 2]
输出:5
问题解读
假设 a = [4,3,2,3,4], 如果把小朋友带有的糖果数量想象成柱子, 糖果传递的方向就是柱子下降的方向,直到遇到凹槽才会停下来, 如下蓝色柱子上的数字代表当前第i个小朋友有的糖果数, 第 个糖果会传递到第3个小朋友停止,此时2,3,4小朋友有相同的糖果数,即a=[4,3,3,3,4]之后的第 , , 个糖果会传递到4,3,2小朋友手上,即a=[4,4,4,4,4], 之后的糖果由于没有凹槽,所以都会传递到小U的手上
只有遇到柱子比前一个柱子大的情况糖果才会停下,所以我们自然想到单调栈,维护一个单调递减的单调栈,在遇到糖果数大于栈顶时,就将栈顶弹出,并计算这个凹槽长和宽, 面积即为可以传递的糖果数
遍历小朋友位置, 利用单调栈计算到第i个小朋友之前能传递的糖果总数cur, 如果糖果数cur大于m, 则第m个糖果在此凹槽中,之后通过取模得到m的坐标即可, c++代码及其注释如下
#include <iostream>
#include <vector>
#include<stack>
using namespace std;
int solution(int n, int m, std::vector<int> a) {
// Edit your code here
//维护递减单调栈
stack<int> st;
int cur = 0;//记录目前的糖果数
for(int i = 0; i<n; i++)
{
while(!st.empty()&&a[st.top()]<=a[i])//遇到比栈顶高的元素
{
int index = st.top();//这个是凹槽的底
st.pop();
int height = st.empty()? a[i]-a[index]: min(a[i], a[st.top()])-a[index];//计算凹槽的高
int width = st.empty()? i: i-st.top()-1;//计算凹槽的宽
int area = width*height;//凹槽的面积
if (cur+area>=m)//判断m是否在此凹槽中
return st.empty()? (area-(m-cur))%width+1: st.top()+(area-(m-cur))%width+2;//返回索引+1
cur += area;
}
st.push(i);
}
return n+1;//返回小U的位置
}
python版提交代码
def solution(n, m, a):
st = [] # 维护递减单调栈
cur = 0
for i in range(n):
while st and a[st[-1]] <= a[i]: # 遇到比栈顶高的元素
index = st.pop()
height = (a[i] - a[index]) if not st else min(a[i], a[st[-1]]) - a[index] # 计算凹槽的高
width = i if not st else i - st[-1] - 1 # 计算凹槽的宽
area = width * height
if cur+area >= m: # 判断m是否在此凹槽中
return (area - (m-cur)) % width + 1 if not st else st[-1] + (area - (m-cur)) % width + 2 # 返回索引+1
cur+=area
st.append(i)
return n + 1 # 返回小U的位置