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;
    }
};
posted @ 2022-01-11 12:08  Rogn  阅读(88)  评论(0编辑  收藏  举报