【C++刷题】优选算法——贪心第一辑

  • 什么是贪心算法
    贪心策略:局部最优 -> 全局最优
    “贪婪+鼠目寸光”
    1、把解决问题的过程分为若干步
    2、解决每一步的时候,都选择当前看起来“最优的”解法
    3、“希望”得到全局最优解

  • 贪心算法的特点
    贪心策略的提出是没有标准或者模板的
    正确的贪心策略的提出是要有数学依据的

  1. 柠檬水找零
    在这里插入图片描述
class Solution {
    unordered_map<int, int> change = {
        {5, 0},
        {10, 0},
        {20, 0},
    };
public:
    bool lemonadeChange(vector<int>& bills) {
        for (int e : bills) {
            if (e == 5) {
                change[5]++;
            } else if (e == 10) {
                if (change[5] >= 1) {
                    change[5]--;
                    change[10]++;
                } else {
                    return false;
                }
            } else {
                if (change[10] >= 1 && change[5] >= 1) {
                    change[5]--;
                    change[10]--;
                    change[20]++;
                }
                else if (change[5] >= 3) {
                    change[5] -= 3;
                    change[20]++;
                } else {
                    return false;
                }
            }
        }
        return true;
    }
};
  1. 将数组和减半的最少操作次数
    在这里插入图片描述
class Solution {
    double sum = 0;
    double mid = 0;
    int count = 0;
    priority_queue<double> pq;
public:
    int halveArray(vector<int>& nums) {
        for (int e : nums) {
            sum += e;
            pq.push(e);
        }

        mid = sum / 2;
        sum = 0;
        while (sum < mid) {
            double top = pq.top(); pq.pop();
            top /= 2;
            sum += top;
            ++count;
            pq.push(top);
        }
        return count;
    }
};
  1. 最大数
    在这里插入图片描述
class Solution {
    string ret;
    vector<string> v;
public:
    string largestNumber(vector<int>& nums) {        
        for (int e : nums) {
            v.push_back(to_string(e));
        }
        ranges::sort(v, [](const string& left, const string& right){
            return left + right > right + left;
        });
        if (v.front() == "0") return "0";

        for (const string& s : v) {
            ret += s;
        }
        
        return ret;
    }
};
  1. 摆动序列
    在这里插入图片描述
// 解法一
class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {

        int left = nums[0];
        int count = 1;
        int right;
        bool flag;

        int i = 1;
        for (; i < nums.size();) {
            if (nums[i] > left) {
                right = nums[i];
                ++i;
                while (i < nums.size() && nums[i] >= right) {
                    right = nums[i];
                    ++i;
                }
                ++count;
                flag = true; // true 为上升
                left = right;
                break;
            } else if (nums[i] < left) {
                right = nums[i];
                ++i;
                while (i < nums.size() && nums[i] <= right) {
                    right = nums[i];
                    ++i;
                }
                ++count;
                flag = false; // false 为下降
                left = right;
                break;
            } else {
                ++i;
            }
        }

        for (; i < nums.size();) {
            if (flag) { // 前一个是上升
                if (nums[i] < left) {
                    right = nums[i];
                    ++i;
                    while (i < nums.size() && nums[i] <= right) {
                        right = nums[i];
                        ++i;
                    }
                    ++count;
                    flag = !flag;
                    left = right;
                } else {
                    ++i;
                }
            } else {
                if (nums[i] > left) {
                    right = nums[i];
                    ++i;
                    while (i < nums.size() && nums[i] >= right) {
                        right = nums[i];
                        ++i;
                    }
                    ++count;
                    flag = !flag;
                    left = right;
                } else {
                    ++i;
                }
            }
        }
        return count;
    }
};

// 解法二
class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if (nums.size() < 2) return 1;

        int ret = 0, left = 0;
        for (int i = 1; i < nums.size(); ++i) {
            int right = nums[i] - nums[i-1];
            if (right == 0) continue;
            if (left * right <= 0) ++ret;
            left = right;
        }
        return ret + 1;
    }
};
  1. 最长递增子序列
    在这里插入图片描述
    存什么:所有长度为 n 的递增子序列中,最后一个元素的最小值
    存哪里:所有大于等于 nums[i] 的最小值的位置
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> v = { nums[0] };
        for (int i = 1; i < nums.size(); ++i) {
            if (nums[i] < v.back()) {
                int left = 0, right = v.size() - 1;
                while (left < right) {
                    int mid = left + (right - left) / 2;
                    if (v[mid] < nums[i]) {
                        left = mid + 1;
                    } else {
                        right = mid;
                    }
                }
                v[left] = nums[i];
            } else if (nums[i] > v.back()) {
                v.push_back(nums[i]);
            }
        }
        return v.size();
    }
};
  1. 递增的三元子序列
    在这里插入图片描述
bool increasingTriplet(vector<int>& nums) {
    vector<int> tuple = { nums[0] };
    for (int i = 1; i < nums.size(); ++i) {
        if (nums[i] > tuple.back()) {
            tuple.push_back(nums[i]);
            if (tuple.size() == 3) return true;
        } else {
            for (int j = 0; j < tuple.size(); ++j) {
                if (nums[i] <= tuple[j]) {
                    tuple[j] = nums[i];
                    break;
                }
            }
        }
    }
    return false;
}

// 简化
bool increasingTriplet(vector<int>& nums) {
    int num1 = nums[0], num2 = INT_MAX;
    for (int i = 0; i < nums.size(); ++i) {
        if (nums[i] > num2) return true;
        else if (nums[i] <= num1) num1 = nums[i];
        else num2 = nums[i];
    }
    return false;
}
  1. 最长连续递增序列
    在这里插入图片描述
class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int ret = 0;
        int left = 0, right = 1;
        while (right < nums.size()) {
            int cur_len = 0;
            while (right < nums.size() && nums[left++] < nums[right++]) {
                ++cur_len;
            }
            ret = max(ret, cur_len);
        }
        return ret + 1;
    }
};
  1. 买卖股票的最佳时机
    在这里插入图片描述
int maxProfit(vector<int>& prices) {
    int ret = 0;
    int prev_min = prices[0];
    for (int i = 1; i < prices.size(); ++i) {
        ret = max(ret, prices[i] - prev_min);
        prev_min = min(prev_min, prices[i]);
    }
    return ret;
}
  1. 买卖股票的最佳时机 II
    在这里插入图片描述
int maxProfit(vector<int>& prices) {
    int ret = 0;
    for (int i = 1; i < prices.size(); ++i) {
        if (prices[i] > prices[i-1]) ret += (prices[i] - prices[i-1]);
    }
    return ret;
}
  1. K 次取反后最大化的数组和
    在这里插入图片描述
class Solution {
    int ret = 0;
    int plus_min = INT_MAX;
    vector<int> minus;
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        for (int e : nums) {
            if (e < 0) minus.push_back(e);
            else {
                ret += e;
                plus_min = min(plus_min, e);
            }
        }

        if (k > minus.size()) {
            for (int e : minus) {
                ret += -e;
                plus_min = min(plus_min, -e);
            }
            k -= minus.size();
            if (k % 2) ret -= 2 * plus_min;
        } else {
            ranges::sort(minus);
            for (int i = 0; i < k; ++i) ret += -minus[i];
            for (int i = k; i < minus.size(); ++i) ret += minus[i];
        }
        return ret;
    }
};
  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿阿阿顺Yaya

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

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

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

打赏作者

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

抵扣说明:

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

余额充值