优先队列/堆 题目讲解
cpp
洛谷
P1628
p1334
p1090
p12802
p2278
priority_queue(优先队列/堆)
cpp
//大根堆
priority_queue<int> heap;
//小根堆
priority_queue<int,vector<int>,greater<int>> heap;
heap.push(x);//插入一个数x
heap.top();//返回堆顶元素
heap.pop();//弹出堆顶元素
pq.emplace(people[j].r, people[j].idx);等效于pq.push(make_pair(people[j].r, people[j].idx));
在优先队列 pq 中,直接构造一个 pair(r, idx) 对象,
p1628
小根堆读入符合要求的字符串然后依次读,弹就行
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin>>n;
vector<string> a(n);
for(int i = 0;i<n;i++) cin>>a[i];
string T;
cin>>T;
priority_queue<string,vector<string>,greater<string>> q;
for(int i = 0;i<n;i++){
if(a[i].size()>=T.size() && a[i].substr(0,T.size())==T){
q.push(a[i]);
}
}
while(!q.empty()){
cout<<q.top()<<"\n";
q.pop();
}
}
p1334
倒过来看,等价于不断合并两块木板,合并代价是两块之和,得到代价总和最小,那就贪心思想每次取最小两块合并。同样可以用小根堆解决。
cpp
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin>>n;
priority_queue<ll,vector<ll>,greater<ll>> q;
for(int i = 0;i<n;i++){
ll x;
cin>>x;
q.push(x);
}
ll ans = 0;
while(q.size()>1){
ll a = q.top(); q.pop();
ll b = q.top(); q.pop();
ll s = a+b;
ans += s;
q.push(s);
}
cout<<ans;
}
p1090
这题与上一题本质完美一模一样,甚至代码都不用改的。就是上一题倒过来的思维
cpp
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin>>n;
priority_queue<ll,vector<ll>,greater<ll>> q;
for(int i = 0;i<n;i++){
ll x;
cin>>x;
q.push(x);
}
ll ans = 0;
while(q.size()>1){
ll a = q.top(); q.pop();
ll b = q.top(); q.pop();
ll s = a+b;
ans += s;
q.push(s);
}
cout<<ans;
}
p12802
也是贪心加优先队列,对于入侵的,肯定优先入侵好打的(人少的)。贪心优先入侵小星球积累资源,再用大根堆动员最大舰队以最少次数征服剩余星球。
cpp
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin>>T;
while(T--){
int n;
ll k;
cin>>n>>k;
vector<ll>a(n);
ll sum = 0;
for(int i = 0;i<n;i++){
cin>>a[i];
sum += a[i];
}
if(k>=sum){
cout<<0<<"\n";
continue;
}
sort(a.begin(),a.end());
priority_queue<ll> q;
ll temp = 0;
ll ans = 0;
int p = 0;
while(1){
while(p<n && a[p]<=k+temp){
q.push(a[p]);
p++;
}
if(q.empty()){
cout<<-1<<"\n";
break;
}
temp += q.top();
q.pop();
ans++;
if(k+2*temp>=sum){
cout<<ans<<"\n";
break;
}
}
}
}
p2278
优先队列+臭模拟,跟前面的不同之处在于要自定义一下priority_queue的排序规则
cpp
#include<bits/stdc++.h>
using namespace std;
struct node{
int id,st,re,pr;
bool operator < (const node &a)const{
if(pr==a.pr) return st>a.st; // 优先级相同,先到的优先
else return pr<a.pr; // 优先级大的优先
}
};
node c;
long long ti; // 当前时间
priority_queue<node> q; // 等待队列(大根堆)
int main(){
while(cin>>c.id>>c.st>>c.re>>c.pr){
// 先把当前能在新进程到达前执行完的进程全部跑完
while(!q.empty() && ti+q.top().re<=c.st){
node b=q.top(); q.pop();
cout<<b.id<<" "<<ti+b.re<<endl; // 输出完成时间
ti+=b.re; // 时间推进
}
// 若当前进程还没跑完,被新进程抢占
if(!q.empty()){
node d=q.top(); q.pop();
d.re=d.re-(c.st-ti); // 减去已经执行的时间
q.push(d); // 放回等待队列
}
q.push(c); // 新进程入队
ti=c.st; // 时间跳到新进程到达时刻
}
// 输入结束后,把剩余进程全部跑完
while(!q.empty()){
node f=q.top(); q.pop();
ti+=f.re;
cout<<f.id<<" "<<ti<<endl;
}
return 0;
}