leetCode刷题---贪心算法【2020第一版】

#诗经·秦风·驷驖
驷驖(tie3)孔阜(fu4),六辔(pei4)在手。公之媚子,从公于狩。
奉时辰牡,辰牡孔硕。公曰左之,舍拔则获。
游于北园,四马既闲。輶(you2)车鸾镳(biao3),载猃(xian3)歇骄。

leetCode刷题---贪心算法【2020第一版】_第1张图片

0.贪心算法概述

\qquad 从问题的初始状态出发,设定某种规律,不断进行贪心选择取得当前最优,最终得到整个问题的(一个)最优解。

1.分糖果

Q:leetCode刷题---贪心算法【2020第一版】_第2张图片
A:设定贪心规律:

  1. s[i]不能满足某个g[j],则不能满足g[j+1]及后续;
  2. g[j]如果能用s[i]满足,则不需要用s[i+1]及后续更大来满足,因此可以保留更大的来满足后续的g[j+1];【贪心
  3. 对g[j]从小到大满足,优先配给小的g[j].【贪心
#include
int findContent(vector<int>& g, vector<int>& s) 
{	
	sort(g.begin(),g.end());
	sort(s.begin(),s.end());
	int g_index = 0;
	int s_index = 0;
	while(g_index<g.size()&&s_index<s.size())
	{
		if(g[g_index]<=s[s_index]) ++g_index;
		++s_index;
	}
	return g_index;
}

2.摆动序列

leetCode刷题---贪心算法【2020第一版】_第3张图片
A: 利用有限装态转移的逻辑
leetCode刷题---贪心算法【2020第一版】_第4张图片

 int wiggleMaxLength(vector<int>& nums)
{
	if(nums.size()<2)  return nums.size();
	static const int BEGIN = 0;
	static const int UP = 1;
	static const int DOWN = 2;
	int max_length = 1;
	int STATE = BEGIN;
	for(int i=1; i<nums.size(); ++i)
	{
		switch(STATE)
		{
		case BEGIN:
			if(nums[i]>nums[i-1]) 
			{
				STATE = UP;
				++max_length;
			}
			else if(nums[i]<nums[i-1])
			{
				STATE = DOWN;
				++max_length;
			}
			//在==状态不做任何转移
			break;
		}
		case UP:
		{
			if(nums[i]<nums[i-1])
			{
				STATE = DOWN;
				++max_length;
			}
			break;			
		}
		case DOWN:
		{
			if(nums[i]>nums[i-1])
			{
				STATE = UP;
				++max_length;
			}
			break;			
		}		
	}
	return max_length;
}

3.移除k个数字

leetCode刷题---贪心算法【2020第一版】_第5张图片
A:通过举例来发掘贪心规律:
高位向低位遍历,如果对应的数字大于下一位数字,则将该位数去掉,得到的数字最小
解决问题:

  1. 字符串遍历完,k>0,如何处理:s=“12345”,k=3;
  2. 数字中有"0"出现,如何处理,s=“100200”,k=1;
  3. 如何存储字符串并返回。
#include
#include
string removeKdigits(string num, int k) 
{
	vector<int> vec_stack;  //利用数组模拟栈,好遍历
	string result = "";
	for(int i=0; i<num.size(); ++i)
	{
		int number = num[i] - '0';
		while(vec_stack.size() && k && vec_stack[vec_stack.size()]>number)
		{
			vec_stack.pop_back();
			--k;
		}
		if(vec_stack.size() || number)
		{
			vec_stack.push_back(number);
		}
	}
	while(vec_stack.size() && k>0)
    {
        vec_stack.pop_back();
        --k;
    }
    for(int i=0; i<vec_stack.size(); ++i)
    {
        result.append(1,'0'+vec_stack[i]);
    }
    if(result=="") return "0";
    return result;
}

4.跳跃游戏

leetCode刷题---贪心算法【2020第一版】_第6张图片
A: 贪心规律:

  1. 列举出第i个位置能够到达的最远位置max_arrive[i] = i + nums[i], 第i位可以到达i+1,i+2,…,i+nums[i];
  2. 从第i位跳至max_arrive[j]中最大的位置;
  3. 从i出发,能够达到的位置配给最理想。
bool canJump(vector<int>& nums) 
{
	vector<int> max_arrive;
	for(int i=0; i<nums.size(); ++i)
	{//构建能够达到最远的数组max_arrive
		max_arrive.push_back(i+nums[i]);
	}
	int at = 0; //遍历max_arrive数组
	int max_index; //记录历史最远位置
	while(at<nums.size() && at<=max_index)
	{
		if(max_index<max_arrive[at])
			max_index = max_arrive[at];
		++at;
	}
	if(at==nums.size()) return true;
	return false;
}

//优化代码
bool canJump(vector<int>& nums)
{
	int max_arrive = 0; //记录最远能到达的位置
	for(int i=0; i<nums.size(); ++i)
	{
		if(i>k)  return false;
		max_arrive = max(k,nums[i]+i);
	}
	return true;
}

leetCode刷题---贪心算法【2020第一版】_第7张图片
A贪心规律:在到达某点前一直不跳跃,发现从该点不能跳到更远的地方了,在之前肯定要跳跃;
leetCode刷题---贪心算法【2020第一版】_第8张图片

int jump(vector<int>& nums)
{
	if(nums.size()<=1) return 0;
	int current_max_index = nums[0]; //可达到最远
	int pre_max_index = nums[0];//最远前置节点的最远
	int jump = 1;
	for(int i=1; i<nums.size(); ++i)
	{
		if(current_max_index<i)
		{
			++jump;
			current_max_index = pre_max_index;
			//必须跳跃,并移至前置节点最远位置处
		}
		pre_max_index = max(pre_max_index,nums[i]+i);
	}
	return jump;
}

5.射击气球

leetCode刷题---贪心算法【2020第一版】_第9张图片
A-贪心规律:

  1. 对各个气球进行排序,按左端点从小到大排序;
  2. 维护一个射击区间[start,end],能够击穿更多的气球;
  3. 当前区间无法击穿下一个气球,重新维护新的射击区间;
#include
#include
bool cmp(const vector<int>& a,const <vector>&b)
{
	return a[0]<b[0];//对二维数组的头进行排序
}
int findMinArrowShots(vector<vector<int>>& points)
{
	if(points.empty()) return 0;
	sort(points.begin(), points.end(), cmp);
	int arrow_num = 1;
	int arrow_start = points[0][0];
	int arrow_end = points[0][1];
	for(int i=1; i<points.size(); ++i)
	{
		if(arrow_end>=points[i][0])
		{//在能够贪心的情况下射击更多的气球
			arrow_start = points[i][0];
			if(arrow_end>points[i][1])
			{
				arrow_end = points[i][1];
			}
		}
		else
		{//不能够射击新的气球,则重新开启贪心区间
			++arrow_num; 
			arrow_start = points[i][0];
			arrow_end = points[i][1];
		}
	}
	return arrow_num;
}

6.最优加油问题

leetCode刷题---贪心算法【2020第一版】_第10张图片
A-贪心规律:

  1. 最大堆,来存储经过的加油站的汽油量;
  2. 从起点到终点遍历各个加油站之间的距离d;
  3. 汽油不够走d时,从最大堆中取出一个加油站,直至能够走距离d;
  4. 堆中全部读出不够d,则返回-1;
  5. 走完汽油减少d;
  6. 堆中添加新的加油站油量。
#include
#include
#include
bool cmp(const vector<int>& a, const vector<int>& b)
{
	return a[0]>b[0];
}
int get_min_stop(int L, int P, vector<vector<int>>&stop)
{   //L表示起始距离,P表示起始油量
	//stop表示到终点距离和加油站油量[距终点距离,剩余油量]
	priority_queue<int> Q; //存储油量的最大堆
	int result = 0;
	stop.push_back(vector<int>(0,0)); //终点油量
	for(int i=0; i<stop.size(); ++i)
	{
		int dis = L - stop[i][0];  //dis表示达到下一个加油站的距离
		if(Q.empty()&&P<dis) return -1;
		while(!Q.empty() && P<dis)
		{
			P += Q.top(); //贪心加油
			Q.pop();
			++result;
		}
		P = P-dis;  //油够,路过加油站
		L = stop[i][0]; //距离更新为加油站到终点距离
		Q.push(stop[i][1]);//用最大堆记录油量,以备贪心加油
	}
	return result;
}

7. TODO

  1. 以期coding总是从头开始构思,应该以一般形式构建递推项,才能使得coding顺畅;
  2. 贪心思想还未完全掌握,特别是局部最优和全局最优,与动态规划要区别开。

你可能感兴趣的:(算法刷题)