马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
对顶堆
由一个大根堆和一个小根堆组成,小根堆内里的数永远比大根堆内里的数要大
用途:用于动态维护区间内第k大的数,要比线段树和动态平衡树写起来更简单
好比说我们要维护第k大的数,那么我们肯定是将前k大的数放进小根堆,小根堆的堆顶就是第k大的数
我们来看一下维护第k大的数的代码
1.建立大根堆和小根堆
- priority_queue<int> qmax; //相当于大顶堆
- priority_queue<int,vector<int>,greater<int>> qmin; //相当于小顶堆
复制代码 2.如果小根堆是空的,大概说当前元素大于小根堆的堆顶元素,那么就将当前元素存入小根堆,否则就存入大根堆
- if(qmin.empty()||a>=qmin.top())
- {
- qmin.push(a);
- }
- else
- {
- qmax.push(a);
- }
复制代码 3.大、小根堆的维护,当我们小根堆的元素,不满k个,就将大根堆的堆顶元素弹入小根堆,如果我们当前小根堆的元素超过了k个,就将小根堆的堆顶元素摊入大根堆
- while(qmin.size()>k)
- {
- qmax.push(qmin.top());
- qmin.pop();
- }
- while(qmin.size()<k&&!qmax.empty())
- {
- qmin.push(qmax.top());
- qmax.pop();
- }
复制代码
4.查询第k大的元素,访问小根堆的堆顶元素
例题
P1168 中位数
思绪:去求奇数项的中位数,这个中位数的下标是在不断变化的,很板的一种动态维护第k大的元素的板题,直接套用对顶堆代码即可
- #include<bits/stdc++.h>using namespace std;#define int long longint n,k;int a;priority_queue<int> qmax;priority_queue<int,vector<int>,greater<int> > qmin;signed main(){ cin>>n; for(int i=1;i<=n;i++) { cin>>a; if(i%2==1) { k=(i+1)/2; } if(qmin.empty()||a>=qmin.top())
- {
- qmin.push(a);
- }
- else
- {
- qmax.push(a);
- } while(qmin.size()>k)
- {
- qmax.push(qmin.top());
- qmin.pop();
- }
- while(qmin.size()<k&&!qmax.empty())
- {
- qmin.push(qmax.top());
- qmax.pop();
- } if(i%2==1) { cout<<qmin.top()<<"\n"; } } return 0;}
复制代码 P7072 [CSP-J2020] 直播获奖
思绪:第一眼看上去,获奖分数线为多少?即i*w/100位置上的人的分数是多少,翻译过来照旧第k大的元素是多少,动态维护第k大的对顶堆板题
直接写就行
- #include<bits/stdc++.h>using namespace std;#define int long longint n,k;double w;int a;priority_queue<int>qmax;priority_queue<int,vector<int>,greater<int> >qmin;signed main(){ cin>>n>>w; for(int i=1;i<=n;i++) { cin>>a; int z=floor(i*w/100); k=max(1LL,z); if(qmin.empty()||a>=qmin.top())
- {
- qmin.push(a);
- }
- else
- {
- qmax.push(a);
- } while(qmin.size()<k&&!qmax.empty()) { qmin.push(qmax.top()); qmax.pop(); } while(qmin.size()>k) { qmax.push(qmin.top()); qmin.pop(); } cout<<qmin.top()<<" "; } return 0;}
复制代码 P2085 最小函数值
思绪:我们发现a,b,c都是正数,那么其堆成周-b/2*a,永远都在负数的位置,也就是说,这n个函数,在0~正无穷内都是单调递增的,那么我们可以考虑一种剪枝的O(n*m)的写法,我们起首创建一个大顶堆,存储前k小的元素,我们在最外层遍历n个函数,内层遍历m个数,当n为1的时间,先将m个函数值存进去,然后当后续的函数,如果存在比堆顶元素小的,就存进大顶堆,弹出堆顶
,如果比堆顶元素大,那就立即竣事当前内层循环,由于后面也会比堆顶元素大,。
直到双重循环遍历完,然后输出倒序前m个即可
- //P2085 最小函数值
- #include<bits/stdc++.h>
- using namespace std;
- #define int long long
- int n,m;
- priority_queue<int>qmax;
- int a,b,c;
- int f;
- int ans[200005];
- signed main()
- {
- cin>>n>>m;
- for(int i=1;i<=n;i++)
- {
- cin>>a>>b>>c;
- for(int j=1;j<=m;j++)
- {
- f=a*j*j+b*j+c;
- if(i==1)
- {
- qmax.push(f);
- }
- else
- {
- if(f<qmax.top())
- {
- qmax.pop();
- qmax.push(f);
- }
- else
- {
- break;
- }
- }
- }
- }
- for(int i=1;i<=m;i++)
- {
- ans[i]=qmax.top();
- qmax.pop();
- }
- for(int i=m;i>=1;i--)
- {
- cout<<ans[i]<<" ";
- }
- return 0;
- }
复制代码 P1631 序列合并
思绪:和上面谁人一个思绪,我们写一个有剪枝的O(n^2)的写法 ,和上面谁人思绪差不多
- #include<bits/stdc++.h>
- using namespace std;
- #define int long long
- int n;
- int a[100005];
- int b[100005];
- int ans[100005];
- priority_queue<int> q;
- signed main()
- {
- cin>>n;
- for(int i=1;i<=n;i++)
- cin>>a[i];
- for(int i=1;i<=n;i++)
- cin>>b[i];
- sort(a+1,a+1+n);
- sort(b+1,b+1+n);
- int k;
- for(int i=1;i<=n;i++)
- {
- for(int j=1;j<=n;j++)
- {
- k=a[i]+b[j];
- if(i==1)
- {
- q.push(k);
- }
- else
- {
- if(k<q.top())
- {
- q.pop();
- q.push(k);
- }
- else
- {
- break;
- }
- }
- }
- }
- for(int i=1;i<=n;i++)
- {
- ans[i]+=q.top();
- q.pop();
- }
- for(int i=n;i>=1;i--)
- cout<<ans[i]<<" ";
- return 0;
- }
复制代码 P1801 黑匣子
思绪:我们很轻易发现,标题照旧动态求第k小的,因此我们要将前k小的,放入大根堆中,当我们大根堆位空,大概说当前元素小于大根堆的堆顶,就存进来,我们用vis去统计要举行输出的位置即可
- #include<bits/stdc++.h>
- using namespace std;
- #define int long long
- int n,m;
- int a[200005];
- int b[200005];
- int vis[200005];
- priority_queue<int> qmax;
- priority_queue<int,vector<int>,greater<int> > qmin;
- signed main()
- {
- cin>>n>>m;
- for(int i=1;i<=n;i++)
- {
- cin>>a[i];
- }
- for(int i=1;i<=m;i++)
- {
- cin>>b[i];
- vis[b[i]]++;
- }
- int k=0;
- for(int i=1;i<=n;i++)
- {
- if(qmax.empty()||a[i]<qmax.top())
- {
- qmax.push(a[i]);
- }
- else
- {
- qmin.push(a[i]);
- }
- while(vis[i])
- {
- k++;
- while(qmax.size()<k)
- {
- qmax.push(qmin.top());
- qmin.pop();
- }
- while(qmax.size()>k)
- {
- qmin.push(qmax.top());
- qmax.pop();
- }
- cout<<qmax.top()<<"\n";
- vis[i]--;
- }
- }
- return 0;
- }
复制代码
|