力扣【贪心】相关题目

贪心

感觉贪心还是要通过多接触题目积累经验。

53. 最大子数组和 + 输出子数组

如果当前和为负数,那只能作负贡献。因此当和为负数就从下一个元素重新开始算,否则加在上一个连续子数组。

int maxSubArray(vector<int>& nums) {
    int res = -1e9, n = nums.size();
    int st = 0, ed = 0;
    int sum = 0;

    for (int i = 0; i < n; i ++ )
    {
        sum += nums[i];
        if (sum > res) res = sum, ed = i;
        if (sum < 0) 
        {
            sum = 0;
            if (i + 1 < n) st = i + 1;
        }
    }

    cout << st << ' ' << ed << endl;

    return res;
}

376. 摆动序列

求数组中作为摆动序列(一大一小交替)的最长子序列的长度。

去除连续相同的数,之后统计峰谷的数量,首尾一定是峰或谷。

int wiggleMaxLength(vector<int>& nums) {
    nums.erase(unique(nums.begin(), nums.end()), nums.end());
    if (nums.size() <= 2) return nums.size();
    int res = 2;
    for (int i = 1; i + 1 < nums.size(); i ++ )
    {
        int a = nums[i - 1], b = nums[i], c = nums[i + 1];
        if (b > a && b > c || b < a && b < c) res ++;
    }
    return res;
}

55. 跳跃游戏

给定每个位置能跳的最远距离,问能否从起点跳到终点。

维护当前能跳到的最远的位置。

45. 跳跃游戏Ⅱ1345. 跳跃游戏Ⅳ都推荐用 bfs 来求解。

bool canJump(vector<int>& nums) {
    for (int i = 0, j = 0; i < nums.size(); i ++ )
    {
        if (i > j) return false;
        j = max(j, i + nums[i]);
    }
    return true;
}

134. 加油站

如果可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。如果存在解,则保证它是唯一的。

首先如果从加油站 i 最多只能到加油站 j 的话,那么任意 i <= k <= j 作为出发点都无法抵达终点,因为到加油站 k 时至少带着一定的油量 left 此时都无法抵达 j + 1,那作为出发点更不可能到达。

因此如果有唯一解的情况下,该解一定存在于 j + 1 及其之后的加油站。

int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
    int res = 0, cur = 0, tot = 0;
    int n = gas.size();
    for (int i = 0; i < n; i ++ )
    {
        cur += gas[i] - cost[i];
        tot += gas[i] - cost[i];
        if (cur < 0)
        {
           res = i + 1;
           cur = 0;
        }
    }
    if (tot < 0) return -1;
    return res;
}

135. 分发糖果

需要按照以下要求,给孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请给每个孩子分发糖果,计算并返回需要准备的最少糖果数目。

需要基于两边来定的,可以先考虑一边,再去考虑另一边,也就是两遍贪心的做法。

int candy(vector<int>& ratings) {
    int n = ratings.size();
    vector<int> res(n + 1, 1);

    for (int i = 1; i < n; i ++ )
        if (ratings[i] > ratings[i - 1]) res[i] = res[i - 1] + 1;

    for (int i = n - 2; i >= 0; i -- )
        if (ratings[i] > ratings[i + 1]) res[i] = max(res[i], res[i + 1] + 1);

    int ans = 0;
    for (int i = 0; i < n; i ++ ) ans += res[i];

    return ans;
}

56. 合并区间

vector<vector<int>> merge(vector<vector<int>>& regs) {
     vector<vector<int>> res;
     int st = -2e9, ed = -2e9;
     sort(regs.begin(), regs.end());

     for (auto reg : regs)
     {
         int l = reg[0], r = reg[1];
         if (l <= ed) ed = max(ed, r);
         else
         {
            if (st != -2e9) res.push_back({st, ed});
            st = l, ed = r;
         }
     }
     if (st != -2e9) res.push_back({st, ed});

     return res;
}

AcWing 905. 区间选点 452. 用最少数量的箭射爆气球

AcWing 908. 最大不相交区间数量

选择尽量少的点,使得每个区间内至少包含一个选出的点。

选择尽可能多的区间,使选择的区间之间两两没有交集。

有多个活动,每个活动有起始时间和结束时间,要求时间不冲突的情况下参加尽可能多的活动。

按区间右端点从小到大排序,左到右遍历,每个未被覆盖的区间选择右端点,因为右端点可以覆盖更多的区间。

bool cmp(PII &a, PII &b)
{
    return a.y < b.y;
}

int main()
{
    sort(regs.begin(), regs.end(), cmp);
    
    int res = 0, cur = -2e9;
    
    for (int i = 0; i < n; i ++ )
        if (cur < regs[i].x)
        {
            res ++;
            cur = regs[i].y;
        }
    
    cout << res << endl;
    
    return 0;
}

738. 单调递增的数字

要保证每一位上的数字单调不减,分为以下两种情况:

  • 原数字本来就单调不减,那原来的数字就是答案。

  • 原来数字出现减小的情况,要找到最大的单调递增的数,那原本就单调不减的部分一定保持不变,如果其中有相同的部分则找到相同部分的起始位置 kstr[k] -- 且之后的数位全置为 9

在这里插入图片描述

int monotoneIncreasingDigits(int n) {
    string str = to_string(n);
    int k = 0;
    while (k < str.size() - 1 && str[k] <= str[k + 1]) k ++;
    if (k == str.size() - 1) return n;
    while (k && str[k] == str[k - 1]) k --;
    str[k] --;
    for (int i = k + 1; i < str.size(); i ++ ) str[i] = '9';
    return stoi(str);
}

AcWing 906. 区间分组

若干个区间进行分组,要求组数最少且组内区间无交集。

公司安排面试,给出每个应试者各自有空的时间段,它们之间可能有交集,要求对他们进行分组,组内时间不冲突,求最小的组数。

按区间左端点从小到大排序,从左到右遍历区间,判断能否把当前区间放到目前已有的组中(判断区间左端点和每组的右端点大小),如果能则放入,否则新开一个组。

vector<PII> regs;
priority_queue<int, vector<int>, greater<int>> q;

sort(regs.begin(), regs.end());
    
for (int i = 0; i < n; i ++ )
{
    int l = regs[i].x, r = regs[i].y;
    if (q.empty() || q.top() >= l) q.push(r);
    else q.pop(), q.push(r);
}
    
cout << q.size() << endl;

AcWing 907. 区间覆盖

给定一个区间 [st, ed] 和若干区间,要求从若干区间中选择最少数量的区间覆盖给定的区间。

按区间左端点从小到大排序,从前往后依次枚举每个区间,在所有能覆盖 st 的区间中,选择右端点最大的区间并更新 st 的值。

可以通过双指针算法来完成上述操作。

sort(segs.begin(), segs.end());

int res = 0;
bool hasFind = false;
for (int i = 0; i < n; i ++ )
{
    int j = i, r = -2e9;
    while (j < n && segs[j].x <= st)
    {
        r = max(r, segs[j].y);
        j ++;
    }
        
    if (r < st) break;
        
    res ++;
    if (r >= ed)
    {
        hasFind = true;
        break;
    }
        
    st = r;
    i = j - 1;
}
    
if(!hasFind) puts("-1");
else printf("%d\n", res);

AcWing 4966. 填充

有一个长度为 n0101 串,其中有一些位置标记为 ?,这些位置上可以任意填充 0 或者 1,请问如何填充这些位置使得这个 0101 串中出现互不重叠的 0011 子串最多,输出子串个数。

左到右扫描,如果能配对就配对。


AcWing 5308. 公路

公路上有 n 个站点,每个站点都可以加油且每个站点只出售整数升的油。一开始在 1 号站点且油箱是空的。

已知车的油箱足够大,可以装下任意多的油,且每升油可以让车前进 d 公里。问开到站点 n 最少要多少钱?

用”赊账“的角度考虑一下问题:只用计算汽车跑到第 i 个站点还欠了多少油,为了买最便宜的油,这些欠的油应该在 1 ~ i-1 站点中价格最低的站点买。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值