有关优先队列问题的一些整理

首先,先说说优先队列重载格式

 

注意:这里的x<y建立出来的优先队列是降序的,大的在前面,可以理解成x<y这个式子如果成立就执行交换操作,让大的那个到前面去。默认优先队列也是降序的。

然后就是做优先队列题目的一些小思路 

1.题目链接:1011-小A与任务_2021秋季算法入门班第五章习题:优先队列、并查集 (nowcoder.com)

 

 

如这道题目该如何用优先队列去解它,那么在此之前我们要先了解优先队列的用途,在做优先队列题目时我们必须要明白优先队列与普通排序的区别。相较于普通sort排序而言,优先队列的优势在于能用比较低的复杂度时更新里面的内容,如果拿sort来实现这个的话,就得插入一个数据重新sort一遍,复杂度是很高的,这就是优先队列的优势所在。

因此明白了这一点以后,我们做题时的思考方向就不能朝着排序,而是朝着贪心方面去了,应为优先队列的这个性质,大部分的题目都会带有贪心的方式来实现这个实时更新的技巧。

比如像这道题目,我们首先按照yi为关键字对任务进行升序排序,应为肯定是先结束的任务我要先去把他做完才行,这是一个贪心,然后开一个优先队列以zi为关键字进行排序。

再次强调我们得用道数据时时更新这一特点,在这题里面体现在:我们遍历排完序以后的任务,完成一个任务以后把这个任务push进优先队列里面,这样就避免了后面没进来任务的干扰,优先队列的头就是目前已经做过的所以任务里面zi最大的那个。然后如果遇到一个要超时的任务,就从队列头花金币换时间,应为zi最大的话金币换时间的价值是最大的。这是一个贪心。

代码实现:

#include<bits/stdc++.h>
using namespace std;
const long long maxx=0x3f3f3f3f3f3f3f3f;
const long long minn=0xc0c0c0c0c0c0c0c0;
const double pi = 4.0*atan(1.0);
#define int long long
#define f(i,n,m) for(long long i=n;i<=m;++i)
#define unf(i,n,m) for(long long i=n;i>=m;--i)
#define kong NULL
#define debug cout<<"sss"<<endl;


int n;
struct ww{
    int z;int x;int y;
};
ww a[200010];
bool pan(ww x,ww y){
    if(x.y!=y.y) return x.y<y.y;
    else return x.x<y.x;
}
struct pand{
    bool operator()(ww x,ww y){
        return x.z<y.z;
    }
};
priority_queue<ww,vector<ww>,pand>que;
void solve() {
cin>>n;
f(i,1,n){
    cin>>a[i].z>>a[i].x>>a[i].y;
}
sort(a+1,a+n+1,pan);

int t=0;
double money=0;
f(i,1,n){
    t+=a[i].x;
    que.push(a[i]);
    while(t>a[i].y){
        int u=t-a[i].y;
        ww pp=que.top();
        que.pop();
        u=min(pp.x,u);
        t-=u;
        money+=1.0*u/pp.z;
        pp.x-=u;
        if(pp.x>0)que.push(pp);
    }
}
cout<<fixed<<setprecision(1)<<money<<endl;

}


signed main( )
{
ios::sync_with_stdio(false);
solve();
return 0;
}


 

2.题目链接:

1010-网络优化_2021秋季算法入门班第五章习题:优先队列、并查集 (nowcoder.com

 

 

 这道题目也是一样的,考虑贪心,以l为关键字升序排序一下服务线,然后i从1道n遍历每个人,当i等于l的时候就说明开辟了一条新的服务线让i这个人可以进取,把这条线push进优先队列里面(该队列以r为关键字进行升序排序)。这么操作以后,队列中的元素就代表着遍历到当前位置是,前面那些人1——>i可以取的所以服务线。然后我们拿这些人取填充服务线,按照贪心思想,进去的那个人肯定是去他能去的r最小的服务线里面,把人让进去然后这样跟着维护队列就行了。

代码实现:

#include<bits/stdc++.h>
using namespace std;
const long long maxx=0x3f3f3f3f3f3f3f3f;
const long long minn=0xc0c0c0c0c0c0c0c0;
const double pi = 4.0*atan(1.0);
#define int long long
#define f(i,n,m) for(long long i=n;i<=m;++i)
#define unf(i,n,m) for(long long i=n;i>=m;--i)
#define kong NULL
#define debug cout<<"sss"<<endl;


int n,m;
struct ww{
    int l;int r;int val;
}a[1001000];
struct pan{
    bool operator()(ww x,ww y){
        if(x.r!=y.r)return x.r>y.r;
        else return x.val>y.val;
    }
};
bool pana(ww x,ww y){
    return x.l<y.l;
}
priority_queue<ww,vector<ww>,pan>que;
void solve() {
while(cin>>n>>m){int fact=0;
while(!que.empty())que.pop();
f(i,1,m){
    cin>>a[i].l>>a[i].r>>a[i].val;
}
sort(a+1,a+m+1,pana);
int cnt=1;
f(i,1,n){
    while(a[cnt].l==i){
        que.push(a[cnt]);
        cnt++;
    }
    while(!que.empty()&&(que.top().val==0||que.top().r<i))que.pop();
    if(que.empty())continue; 
    fact++;
    ww pp=que.top();
    que.pop();
    pp.val--;
    que.push(pp);
}
cout<<fact<<endl;
}


}

signed main( )
{
ios::sync_with_stdio(false);
solve();
return 0;
}

3.题目链接:1009-Operating System_2021秋季算法入门班第五章习题:优先队列、并查集 (nowcoder.com)


 

 

 这题就更加典型了,记录每个元素对应相同元素下一次出现的位置,按照贪心思想,在里面满了以后如果要替换,肯定是换掉后面不再出现的元素或者是出现的比较晚的元素,因此开优先队列以元素下一次出现的位置为关键字降序排一下,要踢出元素的时候拿头元素就行了。

代码实现:

#include<bits/stdc++.h>
using namespace std;
const long long maxx=0x3f3f3f3f3f3f3f3f;
const long long minn=0xc0c0c0c0c0c0c0c0;
const double pi = 4.0*atan(1.0);
#define int long long
#define f(i,n,m) for(long long i=n;i<=m;++i)
#define unf(i,n,m) for(long long i=n;i>=m;--i)
#define kong NULL
#define debug cout<<"sss"<<endl;
struct ww{
    int val;int next;
};
ww a[50010];
int n,m,q;
map<int,int>mp;
map<int,int>kan;
struct panpan{
    bool operator()(ww x,ww y){
        return x.next<y.next;
    }
};
priority_queue<ww,vector<ww>,panpan>que;
void solve() {
while(cin>>n>>m>>q){
    f(i,1,q){
    cin>>a[i].val;
}
unf(i,q,1){
    if(!mp[a[i].val]){
        a[i].next=600000;
    }
    else {
        a[i].next=mp[a[i].val];
    }
    mp[a[i].val]=i;
}
int fact=0;
int cnt=0;
f(i,1,q){
    if(!kan[a[i].val]||kan[a[i].val]==0){
        fact++;
        if(cnt<n){
            cnt++;
            que.push(a[i]);
            kan[a[i].val]=1;
        }
        else {
            kan[que.top().val]=0;
            que.pop();
            que.push(a[i]);
            kan[a[i].val]=1;
        }
    }
    else {
        que.push(a[i]);
        kan[a[i].val]=1;
    }
    
}

cout<<fact<<endl;
mp.clear();
while(!que.empty())que.pop();
kan.clear();
}

}

signed main( )
{
ios::sync_with_stdio(false);
solve();
return 0;
}


这里是这两天做优先队列专题做下来的一些汇总,我很少有写博客的习惯,但就算是整理整理我这段时间学的东西了,学程序是一条漫长的路,现在处于初学状态,再加上还是自学,学习的东西多而杂,很多时候今天学的东西过半个月已经完全没印象了,于是我就像这借博客这个平台来整理整理,记个笔记,以后也好复习

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值