优先队列

区间分组:
左端点排序,维护每个分组的右端点,如果发现当前区间的左端点比最小的右端点大的话,那么可以把这个区间更新到当前组中;
[https://ac.nowcoder.com/acm/contest/22904/1010]
思路是优先把这个点放在所有右端点当前点大,并且左端点比较小的区间中,因为每个点都是逐渐增大,那么要尽可能用到比较左边的区间中,因为后面的点可能用不到这些区间,从而使后面的点可选择的区间多,也就是可扩展性强

#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010;
struct T {
    int l, r, v;
    bool operator<(const T &t)const {
        if (r != t.r) return r > t.r;
        return l > t.l;
    }
};
T a[N];
bool vis[N];
int main() {
    int n, m;
    while (cin >> n >> m) {
        for (int i = 1; i <= n; i++) vis[i] = false;
        priority_queue<T> q;
        for (int i = 1; i <= m; i++) {
            auto &[x, y, z] = a[i];
            cin >> x >> y >> z;
            q.push(a[i]);
        }
        int ans = 0;
        //在r大于i的所有区间中优先选择l比较小的区间,这样的选择的扩展性最好
        for (int i = 1; i <= n; i++) {
            while (q.size() and q.top().r < i) q.pop();
            if (q.size()) {
                auto t = q.top();
                vis[i] = true;
                q.pop();
                if (t.v - 1 > 0) {
                    q.push({t.l, t.r, t.v - 1});
                }
            }
        }
        for (int i = 1; i <= n; i++) if (vis[i]) ans++;
        cout << ans << endl;
    }
    return 0;
}

tokitsukaze and Soldier
直观思路是枚举每个s[i],选择前s个最大的数字,这个操作可以使用优先队列来进行,按照s降序排序,每次把第i个加入队列中时,首先把队列中的数字减少到s-1个,每次肯定选择删掉最小的数字,然后把这个数字放进去,枚举过程中得出最大值

#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
const int N=100010;
typedef pair<int,int> pii;
pii a[N];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        auto &[x,y]=a[i];
        cin>>y>>x;
    }
    sort(a+1,a+1+n,greater<pii>());
    priority_queue<int,vector<int>,greater<int>> q;
    long long mmax=0,sum=0;
    for(int i=1;i<=n;i++){
        auto [x,y]=a[i];
        while(q.size()>=x) sum-=q.top(),q.pop();
        q.push(y),sum+=y;
        mmax=max(mmax,sum); 
    }
    cout<<mmax<<endl;
    
    return 0;
}

[JSOI2007]建筑抢修
按照最后的修理时间排序,当发现当前的修理时间小于总时间时,可以尝试把已经修好的建筑中所花费时间最多的那个替换掉,如果能替换则替换;

#include <iostream>
#include <queue>
#include <algorithm>
#define int long long
using namespace std;
const int N=200010;
pair<int,int> a[N];
signed main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) {
        auto &[x,y]=a[i];
        cin>>y>>x;
    }
    sort(a+1,a+1+n);
    priority_queue<int> q;
    int sum=0;
    for(int i=1;i<=n;i++){
        auto [y,x]=a[i];
        if(sum+x>y){
            auto t=q.top();
            if(x<t) {
                sum-=t;
                q.pop();
                sum+=x;
                q.push(x);
            }
        }else q.push(x),sum+=x;
    }
    cout<<q.size()<<endl;
    
    return 0;
}

第k小
序列只会逐渐增加,所以每次取第k个元素的时候不能从最小开始入手,可以考虑每次只维护一个大小为k的大根堆,每次删除掉比较大的元素,这些元素是一定不会对答案产生影响,所以可以删除

#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
priority_queue<int> q;
int main(){
    int n;
    cin>>n;
    int x;
     int m,op;
    cin>>m;
    int k;
    cin>>k;
    for(int i=1;i<=n;i++) cin>>x,q.push(x);
   
    while(m--){
        cin>>op;
        if(op&1){
            cin>>x;
            q.push(x);
        }else {
            while(q.size()>k) {q.pop();}
            
            if(q.size()==k) cout<<q.top()<<endl;
            else cout<<-1<<endl;
        }
    }
    
    return 0;
}

一般这种题都是有一个权值和一个限制条件,假如是求最大那么应该是枚举每一个限制条件,找到满足限制条件的最大的哪些元素,所以可以按照限制条件排序,然后使用堆来维护

posted @ 2021-11-19 23:58  指引盗寇入太行  阅读(34)  评论(0编辑  收藏  举报