leetcode-贪心算法

1.判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
示例 1:
s = “abc”, t = “ahbgdc”
返回 true.
示例 2:
s = “axc”, t = “ahbgdc”
返回 false.

class Solution 
{
public:
    bool isSubsequence(string s, string t) 
    {
        if (s.size() == 0) { return true; }
        int i = 0, j = 0;
        while (i < s.size() && j < t.size())
        {
            if (s[i] == t[j]) 
            { 
                ++i;
                ++j;
            }
            else { ++j; }
        }
        return i == s.size();
    }
};

2.K 次取反后最大化的数组和
给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)

以这种方式修改数组后,返回数组可能的最大和。

示例 1:
输入:A = [4,2,3], K = 1
输出:5
解释:选择索引 (1,) ,然后 A 变为 [4,-2,3]。

示例 2:
输入:A = [3,-1,0,2], K = 3
输出:6
解释:选择索引 (1, 2, 2) ,然后 A 变为 [3,1,0,2]。

示例 3:
输入:A = [2,-3,-1,5,-4], K = 2
输出:13
解释:选择索引 (1, 4) ,然后 A 变为 [2,3,-1,5,4]。

提示:
1 <= A.length <= 10000
1 <= K <= 10000
-100 <= A[i] <= 100

class Solution {
public:
    int sum_A(vector<int>& A)
    {
        int res = 0;
        for (int i = 0; i < A.size(); ++i) { res += A[i]; }
        return res;
    }
    int largestSumAfterKNegations(vector<int>& A, int K) {
        while (K > 0) {
            sort(A.begin(), A.end());
            A[0] = -A[0];
            --K;
        }
        return sum_A(A);
    }
};

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

注意:
你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。

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

示例 2:
输入: [1,2], [1,2,3]
输出: 2

解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.

class Solution 
{
public:
    int findContentChildren(vector<int>& g, vector<int>& s) 
    {
        if (s.empty() || g.empty()) { return 0; }
        int result = 0;
        sort(s.begin(), s.end());
        sort(g.begin(), g.end());
        int i = 0, j = 0;
        while (i < s.size() && j < g.size())
        {         
            if (s[i] >= g[j])
            {
                ++result;
                ++i;
                ++j;
            }
            else { ++i; }
        }
        return result;
    }
};

4.柠檬水找零
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。

顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

注意,一开始你手头没有任何零钱。

如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

示例 1:
输入:[5,5,5,10,20]
输出:true
解释:
前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
由于所有客户都得到了正确的找零,所以我们输出 true。

示例 2:
输入:[5,5,10]
输出:true

示例 3:
输入:[10,10]
输出:false

示例 4:
输入:[5,5,10,10,20]
输出:false
解释:
前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。
对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。
对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。
由于不是每位顾客都得到了正确的找零,所以答案是 false。

提示:
0 <= bills.length <= 10000
bills[i] 不是 5 就是 10 或是 20

class Solution 
{
public:
    bool lemonadeChange(vector<int>& bills) 
    {
        vector<int>record;
        vector<int>::iterator it;
        if (bills.empty()) { return true; }
        if (bills[0] == 5) { record.push_back(bills[0]); }
        else { return false; }
        int res = 0;
        for (int i = 1;i < bills.size();++i)
        {
            if (bills[i] == 5) { record.push_back(bills[i]); }
            else if (bills[i] == 10)
            {
                vector<int>::iterator it = find(record.begin(), record.end(), 5);
                if (it != record.end()) 
                { 
                    record.erase(it);
                    record.push_back(10);
                }
                else { return false; }
            }
            else
            {
                int num_5 = count(record.begin(), record.end(), 5);
                int num_10 = count(record.begin(), record.end(), 10);
                if (num_5 >= 3 && num_10 == 0)
                {
                    for (int i = 0; i < 3; ++i)
                    {
                        it = find(record.begin(), record.end(), 5);
                        record.erase(it);
                    }
                    record.push_back(20);
                }
                else if (num_10 >= 1 && num_5 >= 1)
                {
                    it = find(record.begin(), record.end(), 5);
                    record.erase(it);
                    it = find(record.begin(), record.end(), 10);
                    record.erase(it);
                }
                else { return false; }
            }
        }
        return true;
    }
};

5.最后一块石头的重量
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。

最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。

示例:
输入:[2,7,4,1,8,1]
输出:1
解释:
先选出 7 和 8,得到 1,所以数组转换为 [2,4,1,1,1],
再选出 2 和 4,得到 2,所以数组转换为 [2,1,1,1],
接着是 2 和 1,得到 1,所以数组转换为 [1,1,1],
最后选出 1 和 1,得到 0,最终数组转换为 [1],这就是最后剩下那块石头的重量。

提示:
1 <= stones.length <= 30
1 <= stones[i] <= 1000

解题思路
比较好理解的一种方法,排序,始终用第一个减去第二个,然后第二个变为0
假如 2 6 4 9
排序后 9 6 4 2
第一次循环(-6) 0 3 4 2,然后排序4 3 2 0
第二次循环 (-3) 1 0 2 0,排序 2 1 0 0
第三次循环(-1) 1 0 0 0,然后第一个就是了
代码

class Solution 
{
public:
    int lastStoneWeight(vector<int>& stones) 
    {
        int stones_size = stones.size();
        sort(stones.begin(), stones.end());
        for (int i = stones_size - 1; i > 0; --i)
        {
            if (stones[stones_size-1] != stones[stones_size-2])
            {
                stones[stones_size - 1] = stones[stones_size - 1] - stones[stones_size - 2];
                stones[stones_size - 2] = 0;
            }
            else 
            {
                stones[stones_size - 1] = 0;
                stones[stones_size - 2] = 0;
            }
            sort(stones.begin(), stones.end());
        }
        return stones[stones_size - 1];
    }
};

6.买卖股票的最佳时机 II
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:
1 <= prices.length <= 3 * 10 ^ 4
0 <= prices[i] <= 10 ^ 4

解题思路:
假设给定的数组为:
[7, 1, 5, 3, 6, 4]
如果我们在图表上绘制给定数组中的数字,我们将会得到:
在这里插入图片描述
如果我们分析图表,那么我们的兴趣点是连续的峰和谷。
用数学语言描述为:
在这里插入图片描述

关键是我们需要考虑到紧跟谷的每一个峰值以最大化利润。如果我们试图跳过其中一个峰值来获取更多利润,那么我们最终将失去其中一笔交易中获得的利润,从而导致总利润的降低。

例如,在上述情况下,如果我们跳过 peaki和 valleyj试图通过考虑差异较大的点以获取更多的利润,获得的净利润总是会小与包含它们而获得的净利润,因为 C总是小于 A+B。

在这种情况下,我们可以简单地继续在斜坡上爬升并持续增加从连续交易中获得的利润,而不是在谷之后寻找每个峰值。最后,我们将有效地使用峰值和谷值,但我们不需要跟踪峰值和谷值对应的成本以及最大利润,但我们可以直接继续增加加数组的连续数字之间的差值,如果第二个数字大于第一个数字,我们获得的总和将是最大利润。这种方法将简化解决方案。
这个例子可以更清楚地展现上述情况:
[1, 7, 2, 3, 6, 7, 6, 7]
与此数组对应的图形是:
在这里插入图片描述
从上图中,我们可以观察到 A+B+CA+B+CA+B+C 的和等于差值 DDD 所对应的连续峰和谷的高度之差。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int maxprofit = 0;
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] > prices[i - 1])
                maxprofit += prices[i] - prices[i - 1];
        }
        return maxprofit;
    }
};

7.两地调度
公司计划面试 2N 人。第 i 人飞往 A 市的费用为 costs[i][0],飞往 B 市的费用为 costs[i][1]。
返回将每个人都飞到某座城市的最低费用,要求每个城市都有 N 人抵达。

示例:
输入:[[10,20],[30,200],[400,50],[30,20]]
输出:110
解释:
第一个人去 A 市,费用为 10。
第二个人去 A 市,费用为 30。
第三个人去 B 市,费用为 50。
第四个人去 B 市,费用为 20。

最低总费用为 10 + 30 + 50 + 20 = 110,每个城市都有一半的人在面试。

提示:
1 <= costs.length <= 100
costs.length 为偶数
1 <= costs[i][0], costs[i][1] <= 1000

class Solution 
{
public:
    /*
    我们这样来看这个问题,公司首先将这 2N 个人全都安排飞往 BBB 市,
    再选出 N 个人改变它们的行程,让他们飞往 A 市。如果选择改变一个人的行程,
    那么公司将会额外付出 price_A - price_B 的费用,这个费用可正可负。
    因此最优的方案是,选出 price_A - price_B 最小的N 个人,让他们飞往 A 市,其余人飞往 B 市。
    算法
    按照 price_A - price_B 从小到大排序;
    将前 N 个人飞往 A 市,其余人飞往 B 市,并计算出总费用。
    */
    int twoCitySchedCost(vector<vector<int>>& costs) 
    {
        int N = costs.size();
        int MinCosts = 0;
        sort(costs.begin(), costs.end(), [](const vector<int>& a, const vector<int>& b) {return (a[0] - a[1] < b[0] - b[1]); });
        for (int i = 0; i < N / 2; ++i) { MinCosts += costs[i][0] + costs[i + N/2][1]; }
        return MinCosts;
    }
};

8.玩筹码
数轴上放置了一些筹码,每个筹码的位置存在数组 chips 当中。
你可以对 任何筹码 执行下面两种操作之一(不限操作次数,0 次也可以):
将第 i 个筹码向左或者右移动 2 个单位,代价为 0。
将第 i 个筹码向左或者右移动 1 个单位,代价为 1。

最开始的时候,同一位置上也可能放着两个或者更多的筹码。
返回将所有筹码移动到同一位置(任意位置)上所需要的最小代价。

示例 1:
输入:chips = [1,2,3]
输出:1
解释:第二个筹码移动到位置三的代价是 1,第一个筹码移动到位置三的代价是 0,总代价为 1。

示例 2:
输入:chips = [2,2,2,3,3]
输出:2
解释:第四和第五个筹码移动到位置二的代价都是 1,所以最小总代价为 2。

提示:
1 <= chips.length <= 100
1 <= chips[i] <= 10^9

class Solution 
{
public:
    int minCostToMoveChips(vector<int>& chips) 
    {
        int odd_num = 0;
        int n = chips.size();
        for (int i = 0; i < n; ++i) 
        {
            if (chips[i] % 2 == 1) { ++odd_num; }
        }
        return min(odd_num, n - odd_num);
    }
};

9.删序造列
给定由 N 个小写字母字符串组成的数组 A,其中每个字符串长度相等。
你需要选出一组要删掉的列 D,对 A 执行删除操作,使 A 中剩余的每一列都是 非降序 排列的,然后请你返回 D.length 的最小可能值。
删除 操作的定义是:选出一组要删掉的列,删去 A 中对应列中的所有字符,形式上,第 n 列为 [A[0][n], A[1][n], …, A[A.length-1][n]])。(可以参见 删除操作范例)

示例 1:
输入:[“cba”, “daf”, “ghi”]
输出:1
解释:
当选择 D = {1},删除后 A 的列为:[“c”,“d”,“g”] 和 [“a”,“f”,“i”],均为非降序排列。
若选择 D = {},那么 A 的列 [“b”,“a”,“h”] 就不是非降序排列了。

示例 2:
输入:[“a”, “b”]
输出:0
解释:D = {}

示例 3:
输入:[“zyx”, “wvu”, “tsr”]
输出:3
解释:D = {0, 1, 2}

提示:
1 <= A.length <= 100
1 <= A[i].length <= 1000

class Solution 
{
public:
    int minDeletionSize(vector<string>& A) 
    {
        int result = 0;
        int rows = A.size();
        int cols = A[0].size();
        for (int j = 0; j < cols; ++j)
        {
            for (int i = 0; i < rows - 1; ++i)
            {
                if (A[i][j] > A[i + 1][j])
                {
                    ++result;
                    break;
                }
            }
        }
        return result;
    }
};

10.分割平衡字符串
在一个「平衡字符串」中,‘L’ 和 ‘R’ 字符的数量是相同的。
给出一个平衡字符串 s,请你将它分割成尽可能多的平衡字符串。
返回可以通过分割得到的平衡字符串的最大数量。

示例 1:
输入:s = “RLRRLLRLRL”
输出:4
解释:s 可以分割为 “RL”, “RRLL”, “RL”, “RL”, 每个子字符串中都包含相同数量的 ‘L’ 和 ‘R’。

示例 2:
输入:s = “RLLLLRRRLR”
输出:3
解释:s 可以分割为 “RL”, “LLLRRR”, “LR”, 每个子字符串中都包含相同数量的 ‘L’ 和 ‘R’。

示例 3:
输入:s = “LLLLRRRR”
输出:1
解释:s 只能保持原样 “LLLLRRRR”.

提示:
1 <= s.length <= 1000
s[i] = ‘L’ 或 ‘R’
分割得到的每个字符串都必须是平衡字符串。

class Solution 
{
public:
    int balancedStringSplit(string s) 
    {
        int balance_count = 0;
        int balance = 0;
        for (int i = 0; i < s.size(); ++i)
        {
            if (s[i] == 'L') { --balance; }
            if (s[i] == 'R') { ++balance; }
            if (balance == 0) { ++balance_count; }
        }
        return balance_count;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yhwang-hub

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值