贪心算法OJ刷题(1)

贪心算法

指所求问题的整体最优解可以通过一系列局部最优的选择来到达。是不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。

贪心算法的基本思路是从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考虑一个数据,它的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中,直到把所有数据枚举完,或者不能再添加算法停止。

该算法不能求解最值问题。

选择排序

它所采用的贪心策略即为每次从未排序的数据中选取最小值并把最小值放在未排序数据的起始位置,直到未排序的数据为0,则结束排序。

在数据结构中我写的选择排序是经过优化的,一次找两个数(最大值和最小值)。但是思路是一样的。

void swap(int* array, int i, int j)
{
    int tmp = array[i];
    array[i] = array[j];
    array[j] = tmp;
}
void selectSort(int* arr, int n){
    int minIndex = 0;
    // i: 未排序数据的起始位置
    for(int i = 0; i < n; i++)
    {
        // 从所有未排序的数据中找最小值的索引
        for(int j = i + 1; j < n; j++)
        {
            if(arr[j] < arr[minIndex]) minIndex = j;
        }
        swap(arr, i, minIndex);
    }
}

分割平衡字符串

题目描述

平衡字符串 中,‘L’ 和 ‘R’ 字符的数量是相同的。给你一个平衡字符串 s,请你将它分割成尽可能多的子字符串,并满足:每个子字符串都是平衡字符串。

返回可以通过分割得到的平衡字符串的 最大数量 。

解题思路

题目要求通过分割得到平衡字符串的最大数量,即尽可能多的分割。

贪心策略: 不要有嵌套的平衡串,只要目前字符串达到平衡,就立即分割。故可以定义一个变量balance,在遇到不同的字符时,向不同的方向变化,当balance为0时达到平衡,分割数更新。

比如:从左往右扫描字符串s,遇到R, balance - 1;遇到L,balance + 1;当balance为0时即,更新记录cnt ++;如果最后cnt==0,说明s只需要保持原样,返回1

class Solution {
public:
    int balancedStringSplit(string s) {
        //贪心策略:遇到平衡的就++,不嵌套
        int count = 0;
        int balance = 0;
        for(auto &e: s)
        {
            if(e == 'L') ++balance;
            else --balance;

            if(balance == 0) ++count;
        }
        return count;
    }
};

买卖股票的最佳时机 II

题目描述

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润 。

解题思路

连续上涨交易日: 则第一天买最后一天卖收益最大,等价于每天都买卖

连续下降交易日: 则不买卖收益最大,即不会亏钱。

故可以遍历整个股票交易日价格列表,在所有上涨交易日都买卖(赚到所有利润),所有下降交易日都不买卖(永不亏钱)。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        //贪心策略:如果第2天比第1天的高,就在第1天买入第二天卖出。低的话就不操作
        int money = 0;
        for(int i = 1; i < prices.size(); ++i)
        {
            if(prices[i-1] < prices[i])
            {
                money += (prices[i] - prices[i-1]);
            }
        }
        return money;
    }
};

跳跃游戏

题目描述

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

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

判断你是否能够到达最后一个下标。

解题思路

设想一下,对于数组中的任意一个位置 y,我们如何判断它是否可以到达?根据题目的描述,只要存在一个位置 i,它本身可以到达,并且它跳跃的最大长度为 i + nums[i],这个值大于等于 y,即 i+nums[i] ≥ y,那么位置y也可以到达。换句话说,对于每一个可以到达的位置 x,它使得 i+1, i+2,⋯, i+nums[i] 这些连续的位置都可以到达。

依照上述思路,我们依次遍历数组中的每一个位置,并实时更新最远可以到达的位置。对于当前遍历到的位置i,如果它在 最远可以到达的位置的范围内,那么我们就可以从起点通过若干次跳跃到达该位置,因此我们可以用 下标i+nums[i]更新 最远可以到达的位置。

在遍历的过程中,如果最远可以到达的位置大于等于数组中的最后一个位置,那就说明最后一个位置可达,我们就可以直接返回True作为答案。反之,如果在遍历结束后,最后一个位置仍然不可达,我们就返回 False 作为答案。

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int maxNum = 0;//目前能到的最远的地方
        int n = nums.size();
        bool can = false;//是否走到目的地
        for(int i = 0; i < n; ++i)
        {
            //每走一步就更新最远能到的下标
            if(maxNum >= i)
            {
                maxNum = max(maxNum, i + nums[i]);
            }
            // 如果最大能够到达的位置已经到达数组末尾,说明必然可以到达
            if(maxNum >= n-1) 
            {
                can = true;
                break;
            }
            // 如果当前最远可以到达的位置未超过当前位置,肯定无法到达数组末尾
            if(maxNum < i) 
            {
                break;
            }
        }
        return can;
    }
};

纸币找零

题目描述

假设1元、2元、5元、10元、20元、50元、100元的纸币分别有c0, c1, c2, c3, c4, c5, c6张。现在要用这些钱来支付K元,最少可以用多少张纸币完成支付?

vector<vector> = { {面值, 张数}, {面值, 张数}, … };

解题思路

用贪心算法的思想,很显然,每一步尽可能用面值大的纸币即可。

#include<iostream>
#include<algorithm>
using namespace std;
int solve(int money,const vector<pair<int,int>>& moneyCount)
{
    int num = 0;
    //首先选择最大面值的纸币
    for (int i = moneyCount.size() - 1; i >= 0; i--)
    {
        //需要的当前面值数量 与 原面值数量 取最小
        //比如张数为0时,就不可以取该面值的纸币
        int c = min(money / moneyCount[i].first, moneyCount[i].second);
        money = money - c * moneyCount[i].first;
        num += c;
    }
    if (money > 0)//说明现有纸币无法完成交易
    	num = -1;
	return num;
}
int main()
{
    //存放纸币与数量: first:纸币,second:数量
    vector<pair<int, int>> moneyCount = { { 1, 3 }, { 2, 1 }, { 5, 4 }, { 10, 3 }, { 20, 0 }, {50, 1}, { 100, 10 } };
    int money;
    cout << "请输入要支付的钱" << endl;
    cin >> money;
    int res = solve(money, moneyCount);
    if (res != -1)
    	cout << res << endl;
    else
    	cout << "NO" << endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值