leecode 解题总结:135. Candy

解决一个关于给孩子们分配糖果的问题,确保每个孩子的糖果数符合其评分,同时满足特定条件下的最小糖果总数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string>
using namespace std;
/*
问题:
There are N children standing in a line. Each child is assigned a rating value.

You are giving candies to these children subjected to the following requirements:

Each child must have at least one candy.
Children with a higher rating get more candies than their neighbors.
What is the minimum candies you must give?

分析: rating value排名。
N个孩子站成一排,每个人被分配了一个排名值。你需要给孩子分配糖果:
每个孩子至少有一个糖果,较高排名的孩子需要比他的邻居都要多。
这里第二点有歧义,较高排名的比左边较低排名的多,比右边较低排名的多。
举例:
r:1 5 2 4 3
设n个人每个人分配的糖果为x[i],i从1到N
根据当前题目:
x[1] < x[2]
x[2] > x[3]
x[3] < x[4]
x[4] > x[5]
难点在于不知道从哪个地方选取赋予最少的糖果,假设我们选取排名最小的第一个孩子,发一个糖果
那么x[1]=1,根据关系式:x[2] >= 2,
    x[3] <= 1,
	x[4] >= 2
	x[5] <= 1
	综上,最少糖果为: 1 + 2 + 1 + 2 + 1 = 7
假设是:
r:1 2 3 4 5
则糖果至少为:1 + 2 + 3 + 4 + 5 = 15
如果是:
r:5 4 3 2 1
糖果也是:5 + 4 + 3 + 2 + 1 = 15
如果是:
r:4 2 1 3 5,
那么糖果是: 3 + 2 + 1 + 2 + 3 = 11
r:4 3 1 5 3
糖果是: 3 + 2 + 1 + 2 + 1 = 9
发现:将一个高排名的左右安排比他排名低的,比一直按排名升序或降序排列好
如果仅仅是给定排名,我们可以得到一个关系式,利用关系式来推出当前第i个孩子,
至少要分配多少糖果。
顺序可以从前向后递推。
如果是: 1 2 6 5 4 3,那么就需要: 1 + 2 + 4 + 3 + 2 + 1 = 13
也就是可以确定发送糖果最多的个数= 寻找到的排名单调增区间或者单调减区间的最大长度
以1 2 6 5 4 3为例找到最大单调递增区间是{1,2,6},最大单调减区间是{6,5,4,3},
所以发给的糖果个数等于4,将该最长的单调增区间或减区间
左右分成两半,对左边继续上述操作,对右边继续寻找上述操作
累加结果。这个应该是分治算法。
如果出现多个相同长度的单调区间,随便选择一个,即可。
dp[i][j]表示从A[i...j]花费的最少糖果数
分析时间复杂度:每次先要找到当前部分中长度最长的单调区间,耗时O(n),
然后将数组分成左边和右边,对左边和右边重复上述过程
总的时间复杂度为O(n^2)

竟然有排名相同的,排名相同的可以给相同的糖果。这个不影响给出的单人最高糖果数量
我需要修改的地方:

比如给定的是
1 1 2 2 3 3:那么长度为6,但是不重复元素数量为3,最高糖果数为3,然后还得遍历一把

输入:
6
1 2 6 5 4 3
5
1 2 3 4 5
5
1 5 2 4 3
12
12 9 2 7 8 6 5 3 4 1 11 10
11
10 9 2 7 8 6 5 10 3 1 11
1
1
2
2 2

3
1 2 2
输出:
13
15
7
23
20
1
2
4

报错:
Input:[1,2,2]
Output:5
Expected:4
为什么?

别人的解法:http://www.cnblogs.com/AndyJee/p/4483043.html
关键:
1
可以从题目角度来看:从前到后,如果后面人排名比前面人高,后面人给的糖果数=前面一个人给的糖果数+1
但是后面<=前面的情况无法判断,我们暂时不处理。
然后从后向前,每个元素与其后面的元素比较,如果ratings[i] > ratings[i+1],且
令candy[i] = max(candy[i] , ratings[i+1]+1),因为至少要比后面的人多1
题目中没有说明如果两个人排名相同是否需要给相同数量的糖果,所以1,2,2.只需要1 + 2 + 1共4颗。

2 典型的双向贪心算法
*/

class Solution {
public:
    int candy(vector<int>& ratings) 
	{
		if(ratings.empty())
		{
			return 0;
		}
		if(ratings.size() <= 1)
		{
			return 1;
		}
		int size = ratings.size();
		vector<int> candyNum(size , 1);//初始化每个人至少一个糖果
		for(int i = 1 ; i < size ; i++)
		{
			//如果从前向后遍历,元素>前面元素,则元素的糖果数=前面元素的糖果数+1
			if(ratings.at(i) > ratings.at(i-1))
			{
				candyNum.at(i) = candyNum.at(i-1) + 1;
			}
		}
		//从后向前遍历, 当前元素>后面元素,则当前元素糖果数=max(当前元素糖果数,后面元素糖果数+1),
		//取较大值,才能满足比两边相邻较小排名分得的糖果数更多一点
		int result = candyNum.at(size - 1);
		//逆序遍历,i--
		for(int i = size - 2; i >= 0; i--)
		{
			if(ratings.at(i) > ratings.at(i+1))
			{
				candyNum.at(i) = max(candyNum.at(i) , candyNum.at(i+1) + 1);
			}
			result += candyNum.at(i);
		}
		return result;
	}
};

void print(vector<int>& result)
{
	if(result.empty())
	{
		cout << "no result" << endl;
		return;
	}
	int size = result.size();
	for(int i = 0 ; i < size ; i++)
	{
		cout << result.at(i) << " " ;
	}
	cout << endl;
}

void process()
{
	 vector<int> nums;
	 int value;
	 int num;
	 Solution solution;
	 vector<int> result;
	 while(cin >> num )
	 {
		 nums.clear();
		 for(int i = 0 ; i < num ; i++)
		 {
			 cin >> value;
			 nums.push_back(value);
		 }
		 num = solution.candy(nums);
		 cout << num << endl;
	 }
}

int main(int argc , char* argv[])
{
	process();
	getchar();
	return 0;
}


/*
public:
	//计算某个单调区间的总糖果数,以及最高糖果数
	int calculate(vector<int>& ratings , int beg , int end, bool isIncreasing)
	{
		if(ratings.empty() || beg < 0 || end >= ratings.size() || beg > end)
		{
			return 0;
		}
		//如果只有一个元素,直接返回1
		if(beg == end)
		{
			return 1;
		}
		
		int result = 0;
		int curNum;
		//如果是递增的
		if(isIncreasing)
		{
			result = 1;
			curNum = 1;
			for(int i = beg + 1 ; i <= end ; i++)
			{
				//后面不等于前面
				if(ratings.at(i) != ratings.at(i-1))
				{
					curNum++;
				}
				//相同元素,curNum不变,
				result += curNum;
			}
		}
		else
		{
			result = 1;
			curNum = 1;
			for(int i = end - 1 ; i >= beg ; i--)
			{
				//后面不等于前面
				if(ratings.at(i) != ratings.at(i+1))
				{
					curNum++;
				}
				//相同元素,curNum不变,
				result += curNum;
			}
		}
		return result;
	}

	int getCandy(vector<int>& ratings , int beg , int end)
	{
		if(ratings.empty() || beg < 0 || end >= ratings.size() || beg > end)
		{
			return 0;
		}
		//如果只有一个元素,直接返回1
		if(beg == end)
		{
			return 1;
		}
		//如果剩余部分从beg到end为单调增区间或减区间,直接返回结果是从1到end-beg的累加和
		int decrementBeg = beg;
		int decrementEnd = beg;
		int incrementBeg = beg;
		int incrementEnd = beg;
		int realDecrementBeg = beg;
		int realDecrementEnd = beg;
		int realIncrementBeg = beg;
		int realIncrementEnd = beg;
		int maxIncrementLen = 1;
		int maxDecrementLen = 1;
		//先计算从beg到end中最长单调增区间和单调减区间,需要记录各自的下标
		for(int i = beg + 1 ; i <= end ; i++)
		{
			//寻找最长单调增区间,相同的元素也包含进来
			if(ratings.at(i) >= ratings.at(i-1))
			{
				incrementEnd = i;
			}
			//说明后面比前面小,是单调减区间,此时应该计算单调增区间的长度,如果遇到相同的元素,不算
			else
			{
				if(incrementEnd - incrementBeg + 1 > maxIncrementLen)
				{
					maxIncrementLen = incrementEnd - incrementBeg + 1 ;
					realIncrementBeg = incrementBeg;
					realIncrementEnd = incrementEnd;
				}
				//需要重新设定单调增区间的起始点和结束点为当前
				incrementEnd = incrementBeg = i;
			}

			//寻找最长单调增区间
			if(ratings.at(i) <= ratings.at(i-1))
			{
				//累加单调减区间的结束值
				decrementEnd = i;
			}
			//说明后面比前面小,是单调减区间,此时应该计算单调增区间的长度
			else
			{
				//计算单调减区间的长度
				if( decrementEnd - decrementBeg + 1 > maxDecrementLen)
				{
					maxDecrementLen = decrementEnd - decrementBeg + 1;
					realDecrementBeg = decrementBeg;
					realDecrementEnd = decrementEnd;
				}
				//更新单调减区间的起始值为当前下标
				decrementBeg = decrementEnd = i;
			}
		}
		//可能存在一种情况,一直到最后一个元素都一直降序或升序,导致遗漏一次,需要计算
		if(incrementEnd - incrementBeg + 1 > maxIncrementLen)
		{
			maxIncrementLen = incrementEnd - incrementBeg + 1 ;
			realIncrementBeg = incrementBeg;
			realIncrementEnd = incrementEnd;
		}
		if( decrementEnd - decrementBeg + 1 > maxDecrementLen)
		{
			maxDecrementLen = decrementEnd - decrementBeg + 1;
			realDecrementBeg = decrementBeg;
			realDecrementEnd = decrementEnd;
		}

		//比较单调增区间和单调减区间谁的长度长,就选择谁的长度作为最高糖果数,并将区间划分
		int realBeg;
		int realEnd;
		int maxLen = max(maxIncrementLen , maxDecrementLen);

		//判断从beg到end是否一直是升序或降序,如果是,就直接计算出结果并返回
		if(maxLen == end - beg + 1)
		{
			int sum = 0;
			if(maxLen == maxIncrementLen)
			{
				sum = calculate(ratings , beg , end , true);
			}
			else
			{
				sum = calculate(ratings , beg , end , false);
			}
			return sum;
		}

		if( maxIncrementLen > maxDecrementLen)
		{
			realBeg = realIncrementBeg;
			realEnd = realIncrementEnd;
		}
		else
		{
			realBeg = realDecrementBeg;
			realEnd = realDecrementEnd;
		}
		//将区间划分成左右不分,
		int leftResult = getCandy(ratings , beg , realBeg - 1);
		int rightResult = getCandy(ratings , realEnd + 1 , end);
		int result = 0;
		if(maxLen == maxIncrementLen)
		{
			result = calculate(ratings , realBeg, realEnd , true);
		}
		else
		{
			result = calculate(ratings , realBeg, realEnd , false);
		}
		result += leftResult + rightResult;
		return result;
	}

    int candy(vector<int>& ratings) {
		if(ratings.empty())
		{
			return 0;
		}
		int result = getCandy(ratings , 0 , ratings.size() - 1);
		return result;
    }
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值