• Leetcode 排序+优先队列+贪心


    有一类题,像有n个任务,n个会议,n颗树,要你按一定的顺序执行,使得总用时最少等,通常都需要想一个贪心策略,然后排序,再用优先队列逐一处理。
    当然贪心是需要证明的,可以先找到一个序,然后证明交换任意两项不会更优。然而比赛的时候更多靠直觉。

    LC 2136. 全部开花的最早一天

    题意:有n颗植物,每颗植物需要先播种plantTime[i]天(不要求连续),再生长growTime[i]天,然后开花,求使所有植物开花的最短时间。
    方法:
    直接说结论:按growtime从大到小排序(感性认识就是,growTime越大提供的planTime的位置也越多,反正planTime也不要求连续,总能将growTime带来的空位填满)
    比赛的时候我猜的growtime-plantTime,不一定最优,看来是完全不用考虑plantTime
    画流水线图,与每行取max

    class Solution {
    public:
        int earliestFullBloom(vector<int>& plantTime, vector<int>& growTime) {
            vector<pair<int, int>> vec;
            for(int i = 0;i < plantTime.size();i++) {
                vec.push_back(make_pair(plantTime[i], growTime[i]));
            }
            sort(vec.begin(), vec.end(), [](pair<int, int>& a, pair<int, int>& b) {
                return a.second > b.second;
            });
            int res = vec[0].first + vec[0].second;
            int sum = 0;
            for(int i = 0;i < vec.size();i++) {
                sum += vec[i].first;
                if(sum + vec[i].second > res) {
                    res = sum + vec[i].second;
                }
            }
            return res;
        }
    };
    

    LC 1834. 单线程 CPU

    题意:n个任务,每次取可行任务中执行时间最短的执行
    方法:先按开始时间排序,枚举时间,每次将能开始的加入队列中,并取队首执行

    class Solution {
    public:
        struct Task {
            int id, enqueueTime, processingTime;
            Task(int id, int enqueueTime, int processingTime) :id(id), enqueueTime(enqueueTime), processingTime(processingTime) {}
        };
        vector<int> getOrder(vector<vector<int>>& tasks) {
            /* 1. 按enqueueTime排序*/
            vector<Task> taskList;
            for(int i = 0;i < tasks.size();i++) {
                taskList.push_back(Task(i, tasks[i][0], tasks[i][1]));
            }
            sort(taskList.begin(), taskList.end(), [](Task& a, Task& b) {
                return a.enqueueTime < b.enqueueTime;
            });
            
            /*2. processingTime 短作业优先*/
            auto cmp = [](const Task& a, const Task& b) {  // 优先队列的比较函数和普通的相反
                if(a.processingTime == b.processingTime)  return a.id > b.id;
                return a.processingTime > b.processingTime;
            };
            priority_queue<Task, vector<Task>, decltype(cmp)> pq(cmp);
            int n = taskList.size();
            long long curTime = 0; /* 3. 枚举时间 */
            int idx = 0;
            vector<int>ans;
            int cnt = 0;
            while(cnt < n) {  // 统计弹出次数,直到n
                // cout << curTime << endl;
                while(idx < n && taskList[idx].enqueueTime <= curTime)  pq.push(taskList[idx++]);
                if(pq.empty())  curTime = taskList[idx].enqueueTime;  // 为空,直接推进时间
                else {
                    Task p = pq.top();pq.pop();
                    ans.push_back(p.id);
                    curTime += p.processingTime;
                    cnt++;
                }
            }
            return ans;
        }
    };
    

    LC 502. IPO

    方法:和上面一题非常类似,也是先能放就放,然后取最大的执行,直到取到k个任务。

    class Solution {
    public:
        typedef pair<int, int> PII;
        int findMaximizedCapital(int k, int w, vector<int>& profits, vector<int>& capital) {
            int n = profits.size();
            vector<PII>tasks;
            for(int i = 0;i < n;i++)  tasks.push_back({capital[i], profits[i]});
            sort(tasks.begin(), tasks.end());
    
            priority_queue<int>pq;
            int cnt = 0;
            int i = 0, curW = w;
            while(i < n || (!pq.empty())) {
                while(i < n && tasks[i].first <= curW) pq.push(tasks[i++].second);  // 能放一直放
                if(!pq.empty()) {
                    auto p = pq.top();pq.pop();  // 取一个最大的
                    curW += p;
                    if((++cnt) >= k)  break;
                } else {
                    break;  
                }
            }
            return curW;
        }
    };
    

    LC1383. 最大的团队表现值

    方法:按工作效率从大到小排序,这样枚举到的每个效率值都是当前最小值。与此同时,将速度用一个大小为k的最小堆维护.

    class Solution {
    public:
        typedef pair<int, int> PII;
        const int mod = 1e9+7;
        int maxPerformance(int n, vector<int>& speed, vector<int>& efficiency, int k) {
            vector<PII>engineers;
            for(int i = 0;i < n;i++)  engineers.push_back({efficiency[i], speed[i]});
            sort(engineers.begin(), engineers.end(), greater<PII>()); // efficiency从大到小
    
            long long cnt = 0, ans = 0, sum = 0;
            priority_queue<int, vector<int>, greater<int>>pq;
            for(int i = 0;i < n;i++) {
                // 维护一个大小为k的小根堆
                if(cnt < k) {pq.push(engineers[i].second); cnt++; sum+=engineers[i].second;}
                else {
                    if(pq.top() < engineers[i].second) {
                        sum -= pq.top();
                        pq.pop();
                        pq.push(engineers[i].second);
                        sum += engineers[i].second;
                    }
                }
                ans = max(ans, engineers[i].first * sum);  // 相当于枚举了最小效率值
            }
            return (int)(ans % mod);
        }
    };
    

    LC 1642. 可以到达的最远建筑

    方法:从前往后,维护一个大小为ladders的堆,且砖块也不够用时返回

    class Solution {
    public:
        int furthestBuilding(vector<int>& heights, int bricks, int ladders) {
            priority_queue<int, vector<int>, greater<int>>pq;
            int sum = 0, bigJump = 0;
            int i = 0;
            while(i < heights.size()-1) {
                cout << bricks << endl;
                int h = heights[i+1] - heights[i];
                if(h <= 0)  i++;
                else {
                    pq.push(h);  // 维护一个大小为ladders的堆
                    if(pq.size() > ladders) {bricks -= pq.top(); pq.pop();} // 大于梯子数就每次换个最小的出来
                    if(bricks < 0)  return i;
                    i++;
                }
            }
            return i;
        }
    };
    

    LC 1005. K 次取反后最大化的数组和

    方法:负数才要反转,维护k个最小的负数,如果k有剩余就去修改绝对值最小的那个数

    class Solution {
    public:
        int largestSumAfterKNegations(vector<int>& nums, int k) {
            int mymin = 100, sum = 0;  
            for(int num : nums) {  // 找绝对值最小值
                if(num < 0)  mymin = min(mymin, -num);
                else  mymin = min(mymin, num);
                sum += num;
            }
            priority_queue<int, vector<int>, greater<int>>pq;
            int cur_sum = 0;
            for(int num : nums) {
                if(num < 0) {
                    if(pq.size() < k) {pq.push(-num); cur_sum += -num;}
                    else {  //维护一个大小为k的最小堆
                        if(-num > pq.top()) {
                            cur_sum -= pq.top(); pq.pop();
                            pq.push(-num); cur_sum += -num;
                        }
                    } 
                }  
            }
            // cout << sum << " " << cur_sum << endl;
            int ans = sum + 2*cur_sum;
            if((k - pq.size()) % 2 == 1) {   // 剩余的k
                ans -= 2*mymin;
            }
            return ans;
        }
    };
    
    个性签名:时间会解决一切
  • 相关阅读:
    Linux下命令行解析
    L.append(a)、L+=[a]和L=L+[a]
    python的lambda表达式
    (转载)2012 EEPW ARM DIY功能扩展模块采买建议(ARM)(PCB)
    (转载)EEPW ARM DIY手记之软件设计(一)——MDK_ARM工程的搭建(ARM)(C)
    (转载)EEPW ARM DIY手记之软件设计(二)——LED跑马灯实验(ARM)(C)
    (转载)EEPW ARM DIY手记之硬件功夫(三)——硬件焊接功夫与硬件测试心得(ARM)(PCB)
    面向对象设计大作业第二阶段:图书馆查书、借书、还书
    C博客作业05指针
    DS博客作业04图
  • 原文地址:https://www.cnblogs.com/lfri/p/15787748.html
Copyright © 2020-2023  润新知