算法【贪心经典题目专题6】

题目一

测试链接:1921. 消灭怪物的最大数量 - 力扣(LeetCode)

分析:这道题就是每次都消灭到达时间最少的怪兽,所以将到达时间进行排序,遍历怪兽数组即可得到答案。代码如下。

class Solution {
public:
    int eliminateMaximum(vector<int>& dist, vector<int>& speed) {
        int length = dist.size();
        vector<int> come;
        come.reserve(length);
        for(int i = 0;i < length;++i){
            come.push_back((dist[i] + speed[i] - 1) / speed[i]);
        }  
        sort(come.begin(), come.end(), [](int& c1, int& c2){
            return c1 < c2;
        });
        int ans = 0;
        for(int time = 0;ans < length;++time){
            if(time < come[ans]){
                ++ans;
            }else{
                break;
            }
        }
        return ans;
    }
};

题目二

测试链接:https://leetcode.cn/problems/largest-palindromic-number/

分析:首先进行词频统计,然后对前导零进行判断,如果1到9的词频都小于等于1,则取0到9中最大的词频不为0的数返回。否则,对于最大的奇数词频,可以在中间有一位;如果没有奇数词频,则不需要。然后从小到大遍历,在答案字符串的两端进行拼接,遍历完即是答案。代码如下。

class Solution {
public:
    int table[10] = {0};
    string largestPalindromic(string num) {
        int length = num.size();
        for(int i = 0;i < length;++i){
            ++table[num[i]-'0'];
        }
        bool flag = true;
        int maxNum = 0;
        int maxOdd = -1;
        for(int i = 0;i < 10;++i){
            if(table[i] != 0){
                maxNum = max(maxNum, i);
            }
            if(i > 0 && table[i] > 1){
                flag = false;
            }
            if(table[i] % 2 == 1){
                maxOdd = max(maxOdd, i);
            }
        }
        if(flag){
            return to_string(maxNum);
        }
        string ans;
        if(maxOdd == -1){
            ans = "";
        }else{
            ans = to_string(maxOdd);
        }
        for(int i = 0;i <= maxNum;++i){
            if(table[i] % 2 == 0 && table[i] != 0){
                ans = string(table[i]/2, i+'0') + ans + string(table[i]/2, i+'0');
            }else if(table[i] % 2 == 1 && table[i] != 1){
                ans = string((table[i]-1)/2, i+'0') + ans + string((table[i]-1)/2, i+'0');
            }
        }
        return ans;
    }
};

题目三

测试链接:1792. 最大平均通过率 - 力扣(LeetCode)

分析:这道题可以利用大根堆维护每一个班级如果来了一个天才可以增加的通过率,然后遍历每一个天才去大根堆堆顶的班级,最终将通过率和除以班级数。代码如下。

class Solution {
public:
    class Class{
        public:
            int pass;
            int total;

            double increase(){
                return ((double)(pass + 1) / (total + 1)) - ((double)(pass) / (total));
            }

            double pass_rate(){
                return (double)(pass) / (total);
            }

        Class(int a, int b){
            pass = a;
            total = b;
        }
    };
    double maxAverageRatio(vector<vector<int>>& classes, int extraStudents) {
        int length = classes.size();
        auto cmp = [](Class& c1, Class& c2){
            return c1.increase() < c2.increase();
        };
        priority_queue<Class, vector<Class>, decltype(cmp)> p(cmp);
        double sum = 0;
        for(int i = 0;i < length;++i){
            Class temp(classes[i][0], classes[i][1]);
            sum += temp.pass_rate();
            p.push(temp);
        }
        for(int i = 0;i < extraStudents;++i){
            Class temp = p.top();
            p.pop();
            sum += temp.increase();
            ++temp.pass;
            ++temp.total;
            p.push(temp);
        }
        return sum / length;
    }
};

题目四

测试链接:857. 雇佣 K 名工人的最低成本 - 力扣(LeetCode)

分析:可以发现,在算工资的时候是以最低期望除以质量最大那名工人来算工资。所以可以先对工人进行排序即最低期望除以质量从小到大排序。然后先将前k-1名工人放入大根堆维护他们的工作质量,从第k名工人开始遍历,因为已经有序,所以这第k名工人和堆里的工人进行工资计算时,是用遍历到的工人的最小期望除以工作质量乘总质量。更新答案之后,将遍历到的工人的工作质量和堆顶的工作质量进行比较,如果比堆顶的工作质量小,则将堆顶的工作质量弹出,遍历到的工作质量入堆。遍历完工人即可得到答案。

class Solution {
public:
    double mincostToHireWorkers(vector<int>& quality, vector<int>& wage, int k) {
        int length = quality.size();
        vector<pair<int, int>> worker;
        worker.reserve(length);
        for(int i = 0;i < length;++i){
            worker.push_back(pair<int, int>(quality[i], wage[i]));
        }
        sort(worker.begin(), worker.end(), [](pair<int, int>& p1, pair<int, int>& p2){
            return ((double)p1.second / p1.first) < ((double)p2.second / p2.first);
        });
        auto cmp = [](int& a, int& b){
            return a < b;
        };
        priority_queue<int, vector<int>, decltype(cmp)> p(cmp);
        int sum = 0;
        for(int i = 0;i < k-1;++i){
            sum += worker[i].first;
            p.push(worker[i].first);
        }
        double ans = (double)(sum + worker[k-1].first) * (double)worker[k-1].second / worker[k-1].first;
        if(!p.empty() && worker[k-1].first < p.top()){
            sum -= p.top();
            sum += worker[k-1].first;
            p.pop();
            p.push(worker[k-1].first);
        }
        for(int i = k;i < length;++i){
            ans = min(ans,
                    (double)(sum + worker[i].first) * (double)worker[i].second / worker[i].first);
            if(!p.empty() && worker[i].first < p.top()){
                sum -= p.top();
                sum += worker[i].first;
                p.pop();
                p.push(worker[i].first);
            }
        }
        return ans;
    }
};

题目五

非负数组前k个最小的子序列累加和。

给定一个数组nums,含有n个数字,都是非负数;给定一个正数k,返回所有子序列中累加和最小的前k个累加和。子序列是包含空集的。

1 <= n <= 10^5  1 <= nums[i] <= 10^6  1 <= k <= 10^5

分析:因为数据量的原因,这道题并不能采用01背包来解。在对于都是非负数的情况下,下面的解才是最优解。首先,最小的累加和最小的子序列是空集,也就是0。然后用一个小跟堆维护累加和和产生这个累加和集合的最后一个下标。每次弹出堆顶元素时有两种可能性的展开,即当前集合对于最后一个下标的数不要,要最后一个下标之后下标的数;或者直接要最后一个下标之后下标的数。然后将这两种可能性产生的集合的最后一个下标和累加和入堆,将答案数组填满即可。代码如下。

class Solution {
public:
    vector<int> kSum(vector<int>& nums, int k) {
        int length = nums.size();
        vector<int> ans;
        ans.reserve(k);
        ans.push_back(0);
        sort(nums.begin(), nums.end());
        auto cmp = [](pair<int, int>& p1, pair<int, int>& p2){
            return p1.second > p2.second;
        };
        priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> p(cmp);
        p.push(pair<int, int>(0, nums[0]));
        int last;
        int sum;
        for(int i = 1;i < k;++i){
            last = p.top().first;
            sum = p.top().second;
            p.pop();
            ans.push_back(sum);
            if(last + 1 < length){
                p.push(pair<int, int>(last+1, sum+nums[last+1]));
                p.push(pair<int, int>(last+1, sum-nums[last]+nums[last+1]));
            }
        }
        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

还有糕手

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

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

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

打赏作者

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

抵扣说明:

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

余额充值