贪心法LeetCode算法例子【总】

本文章记录贪心法的一些 LeetCode 题目,是我学习b站小象学院视频教程所做笔记,文末注明教程出处。侵删 ¯\_( ͡° ͜ʖ ͡°)_/¯

LeetCode [455] 分发饼干

问题描述

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

示例

输入: [1,2,3], [1,1]

输出: 1

解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

解决思路

先把输入的两组数据排序,然后根据每一个饼干从小到大去选择对应可以被满足的孩子

算法代码

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        std::sort(g.begin(),g.end());
        std:sort(s.begin(),s.end());
        int child = 0;
        int cookie = 0;
        while(child<g.size() && cookie<s.size()){
            if(g[child]<=s[cookie]){
                child++;
            }
            cookie++;
        }
        return child;
    }
};

LeetCode [376] 摆动序列

题目描述

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。

例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

示例

示例 1:
输入: [1,7,4,9,2,5]
输出: 6
解释: 整个序列均为摆动序列。

示例 2:
输入: [1,17,5,10,13,15,10,5,16,8]
输出: 7
解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]。

解题思路

使用一个状态机,有三种状态

  1. 开始状态 BEGIN:如果下一个数字比当前的数字要大,则转入上升状态UP;如果下一个数字比当前的数字要小,则转入下降状态DOWN;
  2. 上升状态 UP:如果下一个数字比当前数字更大,则无事发生;如果下一个数字比当前数字小(即符合摆动序列),令max_length++,即长度加一,然后转入下降状态。
  3. 下降状态 DOWN:如果下一个数字比当前数字更小,则无数发生;如果下一个数字比当前数字大,符合摆动序列,则令max_length++,长度加一,然后转入上升状态。
  • 在状态机运行过程中吗,如果有持续上升,或持续下降的情况,则取上升到最大或下降到最小的那个数值,保证后面的数字可以满足摆动序列的概率最大,即涉及到贪心的思想。
  • 套用一个循环遍历,算出最长子序列的长度。

算法代码

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if(nums.size()<2){
            return nums.size(); // 序列个数小于2则本身就是摇摆序列
        }
        // 设置状态机的三种状态
        static const int BEGIN= 0;
        static const int UP = 1;
        static const int DOWN = 2;
        int STATE = BEGIN;
        int max_length = 1; // 先设置最大长度至少为1

        // 从第二个元素开始遍历
        for(int i=1;i<nums.size();i++)
        {
            switch(STATE){
            case BEGIN: // 开始状态
                if(nums[i-1]<nums[i]){
                    STATE = UP;
                    max_length++;
                }
                else if(nums[i-1]>nums[i]){
                    STATE = DOWN;
                    max_length++;
                }
                break;
            case UP:  // 数值上升状态
                if(nums[i-1]>nums[i]){
                    STATE=DOWN;
                    max_length++;
                }
                break;
            case DOWN: // 数值下降状态
                if(nums[i-1]<nums[i]){
                    STATE = UP;
                    max_length++;
                }
                break;
            }
        }
        return max_length;
    }
};


LeetCode [402] 移掉 k 位数字

题目描述

给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。

注意:

  • num 的长度小于 10002 且 ≥ k。
  • num 不会包含任何前导零。

示例

示例 1 :

输入: num = “1432219”, k = 3
输出: “1219”
解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。

示例 2 :

输入: num = “10200”, k = 1
输出: “200”
解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。

解题思路

题目要求剩下的数字组成的数值最大,故每次要留下较小的数字,从最高位开始,尽量取小的数。
即设计一个栈,从第一个元素按顺序加入栈,每次一个数字加入栈要判断该数字大小,如果比栈顶数字大,则直接压入栈,如果比栈顶数字小,则把栈顶出栈,继续判断该值和更新后的栈顶大小。循环直到所有数字都进入栈或者k<=0,如果数字全进入栈了但k依然大于0,则继续将栈做弹出操作直到k=0。

算法代码

class Solution {
public:
    string removeKdigits(string num, int k) {
        std::vector<int> S;  // 用vector作为类似栈结构来使用
        std::string result = "";  // 储存最终结果
        int number = 0;
        for(int i=0; i<num.length(); i++){
            number = num[i] - '0';
            
            // 当栈非空 且 栈顶(这里就是vector最后一个元素,即最后一个数字)大于number 且 k>0(即仍然可以删除数字)
            while(S.size()!=0 && S[S.size()-1]>number && k>0){
                S.pop_back();
                k--;
            }
            if(number!=0 || S.size()!=0){
                S.push_back(number);  // 将数字压入栈
            }
        }

        while(S.size()!=0 && k>0){ // 此时栈不空且还能继续删数字,故继续删至k为0
            S.pop_back();
            k--;
        }
        for(int i=0;i<S.size();i++){  // 储存结果到 result 中
            result.append(1,'0'+S[i]);
        }
        if(result == ""){
            result = "0";
        }
        return result;
    }
};

LeetCode [55] 跳跃游戏

题目描述

定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。

示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

算法思路

先去找出每一个位置作为当前位置时可以跳的最远的位置,程序中即 index[i] = nums[i]+i,使用贪心法思想,则是依次找每一个位置能跳的最远距离 max_index,一次循环更新一次当前能跳的最远距离,直到jump值(遍历到的那个位置)大于max_index, 或者jump已经到达末尾。检查jump如果等于到了末尾则表示可以从位置0到达最后一个位置。

算法代码

class Solution {
public:
    bool canJump(vector<int>& nums) {
        std::vector<int> index; // 计算最远可跳至的位置
        for(int i=0;i<nums.size();i++){
            index.push_back(i+nums[i]);
        }
        int jump = 0;
        int max_index = index[0];
        while(jump<index.size() && jump<=max_index){
            if(max_index<index[jump]){
                max_index = index[jump]; // 保存可以跳的更远的目标位置
            }
            jump++;
        }
        if(jump == index.size()){ // 若jump到达数组尾部,说明可以到达最后一个位置,返回真
            return true;
        }
        return false;  // 否则返回假
    }
};

LeetCode [55] 跳跃游戏2

题目描述

给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例

示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
  从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

算法思路

贪心原理:
每次都跳到当前能跳到的最远位置,如果发现该位置不能跳到末尾,则再选择这之前的一个位置,该位置是除了当前位置能跳最远位置的位置。
其实思路说起来有点绕,看代码吧。

算法代码

class Solution {
public:
    int jump(vector<int>& nums) {
        if(nums.size()<2){ // 如果数组小于2则直接返回0
            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(i>current_max_index){ // 若无法再向前移动移动了,才进行跳跃
                jump++; // 更新跳跃次数
                current_max_index = pre_max_index; // 更新当前可到达的最远位置
            }
            if(pre_max_index<nums[i]+i){
                pre_max_index = nums[i] +i; // 在遍历时如果找到一个能跳更远位置的位置,则更新 pre_max_index
            }
        }
        return jump;
    }
};

LeetCode [452] 用最少数量的箭引爆气球

题目描述

在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。

一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足  xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

示例

Example:

输入:
[[10,16], [2,8], [1,6], [7,12]]

输出:
2

解释:
对于该样例,我们可以在x = 6(射爆[2,8],[1,6]两个气球)和 x = 11(射爆另外两个气球)。

算法思路

初始化一支箭,射爆尽量多的气球

算法代码

bool cmp(const vector<int> &a,const vector<int>&b){
    return a[0] < b[0];
}
class Solution {
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        if(points.size()==0){
            return 0;
        }
        sort(points.begin(),points.end(),cmp);

        int shoot_num = 1;
        int shoot_begin = points[0][0];
        int shoot_end = points[0][1];
        for(int i=1;i<points.size();i++){
            if(points[i][0] <= shoot_end){
                shoot_begin = points[i][0];
                if(shoot_end > points[i][1]){
                    shoot_end = points[i][1];
                }
            }
            else{
                shoot_num++;
                shoot_begin = points[i][0];
                shoot_end = points[i][1];
            }
        }
        return shoot_num;
    }
};

poj [2431] 最优加油方法

题目描述

A group of cows grabbed a truck and ventured on an expedition deep into the jungle. Being rather poor drivers, the cows unfortunately managed to run over a rock and puncture the truck’s fuel tank. The truck now leaks one unit of fuel every unit of distance it travels.

To repair the truck, the cows need to drive to the nearest town (no more than 1,000,000 units distant) down a long, winding road. On this road, between the town and the current location of the truck, there are N (1 <= N <= 10,000) fuel stops where the cows can stop to acquire additional fuel (1…100 units at each stop).

The jungle is a dangerous place for humans and is especially dangerous for cows. Therefore, the cows want to make the minimum possible number of stops for fuel on the way to the town. Fortunately, the capacity of the fuel tank on their truck is so large that there is effectively no limit to the amount of fuel it can hold. The truck is currently L units away from the town and has P units of fuel (1 <= P <= 1,000,000).

Determine the minimum number of stops needed to reach the town, or if the cows cannot reach the town at all.

中文翻译:
一群牛抢了一辆卡车,冒险进入丛林深处探险。由于牛是相当糟糕的司机,不幸的是它们撞到了一块石头,并刺穿了卡车的油箱。现在,卡车每行驶一单位距离就会泄漏一单位燃料。

为了修理卡车,奶牛需要沿着一条长长的蜿蜒的公路,驱车到最近的城镇(距离不超过100万单位)。在这条道路上,在城镇和卡车的当前位置之间,有N (1 <= N <= 10,000)燃料站,奶牛可以在那里停下来获取额外的燃料(1…每站100个)。

丛林对人类来说是个危险的地方,对牛来说尤其危险。因此,奶牛们希望在去镇上的路上尽可能少地停下来加油。幸运的是,他们卡车的油箱容量是如此之大,以至于实际上没有限制它所能容纳的燃料量。卡车目前距离城镇L单位,有P单位的燃料(1 <= P <= 1,000,000)。

确定到达城镇所需的最低停车次数,或者如果奶牛根本无法到达城镇。

示例

Sample Input

4
4 4
5 2
11 5
15 10
25 10

Sample Output

2

算法代码

#include 
#include 
#include 
#include 
using namespace std;

bool cmp(const pair<int, int>& a, const pair<int, int>& b) {
	return a.first > b.first;
}
int get_minimum_stop(int L, int P, vector<pair<int, int>> &stop) {
	priority_queue<int> Q; // 存储有量的最大堆
	int result = 0;
	int dis = 0;
	stop.push_back(make_pair(0, 0));
	sort(stop.begin(), stop.end(), cmp);
	for (int i = 0; i < stop.size(); i++)
	{
		dis = L - stop[i].first;
		while (!Q.empty() && P<dis)
		{
			P += Q.top();
			Q.pop();
			result++;
		}
		if (Q.empty() && P < dis) {
			return -1;
		}
		P = P - dis;
		L = stop[i].first;
		Q.push(stop[i].second);
		
	}
	return result;
}

int main() {
	vector<pair<int, int>> stop;
	int N;
	int L;
	int P;
	int distance;
	int fuel;
	scanf_s("%d", &N);
	for (int i = 0; i < N; i++)
	{
		scanf_s("%d %d", &distance, &fuel);
		stop.push_back(make_pair(distance, fuel));
	}
	scanf_s("%d %d", &L, &P);
	printf("%d\n", get_minimum_stop(L, P, stop));
	return 0;
}

ps: 小象学院教程 https://www.bilibili.com/video/BV1GW411Q77S?t=7029&p=2

你可能感兴趣的:(笔记,算法,算法,数据结构,leetcode,c++)