基本思路
让大根堆的元素<=mid
让小根堆的元素 > mid
显然如果个数是奇数个,大根堆数量要比小根堆多1
否则取出两堆堆顶元素求平均数
P1168 中位数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
板子题
cpp
#include<bits/stdc++.h>
using ll = long long;
using ull = unsigned long long;
using ari = std::array<int, 3>;
using PII = std::pair<int, int>;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
const double eps = 1e-6;
#define int ll
int n,k;
int a[N];
void solve() {
std::cin>>n;
std::priority_queue<int> d;//默认大根堆
std::priority_queue<int,std::vector<int>,std::greater<int> > s;
// 让大根堆的元素<=mid
// 让小根堆的元素>mid
int mid=INT_MIN;
for(int i=1;i<=n;i++)
{
std::cin>>a[i];
if(i==1) mid=a[1];
if(a[i]<=mid)
{
d.push(a[i]);
}else s.push(a[i]);
if(i%2)
{
while((int)d.size()-(int)s.size()<1)
{
d.push(s.top());
s.pop();
}
while((int)d.size()-(int)s.size()>1)
{
s.push(d.top());
d.pop();
}
mid=d.top();
std::cout<<mid<<'\n';
}
}
}
signed main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
int t = 1;
//std::cin>>t;
while (t--) {
solve();
}
return 0;
}
295. 数据流的中位数 - 力扣(LeetCode)
对顶堆
对奇数个偶数个都求中位数
cpp
class MedianFinder {
public:
std::priority_queue<int> l;//默认大根堆
std::priority_queue<int,std::vector<int>,std::greater<int> > r;
MedianFinder() {
}
void addNum(int num) {
double mid=findMedian();
if(mid>100000||num<=mid) l.push(num);
else r.push(num);
int sum=(int)l.size()+(int)r.size();
if(sum%2)
{
while((int)l.size()-(int)r.size()<1){
l.push(r.top());
r.pop();
}
while((int)l.size()-(int)r.size()>1){
r.push(l.top());
l.pop();
}
}else{
while((int)l.size()-(int)r.size()<0){
l.push(r.top());
r.pop();
}
while((int)l.size()-(int)r.size()>0){
r.push(l.top());
l.pop();
}
}
}
double findMedian() {
if(l.size()==0) return 1000000;
if((int)l.size()>(int)r.size()) return l.top();
else return (l.top()+r.top())/2.0;
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
有序集合+双指针
第一次用这个数据结构感觉还蛮好用的。
set、multiset、map、multimap
特点:底层实现是红黑树,键值有序,set 和 map 键不可重复,而 multiset 和 multimap 可重复;
复杂度:插入、删除、查找都为O(logN);
感觉就是自动实现一个有序数组,通过左右两个迭代器来维护中位数的位置。
如果插入之后只有一个元素,毫无疑问,起始迭代器就是中位数。
如果插完是奇数,说明原来是偶数,l和r分别指向两个数字,现在要让他们指向一个数字。
如果要插入的数字刚好>=l且<r,说明左指针要往右边移动,右指针要往左边移动。
如果要插入的数字刚好<l,说明右指针要往左边移动。
如果要插入的数字刚好>=r,说明左指针要往右边移动。
如果插完是偶数,说明原来是奇数,l和r指向同一个数字,现在要让他们分别指向两个数字。
如果当前数字<l,左指针左移
如果当前数字>=l,右指针右移
cpp
class MedianFinder {
std::multiset<int> nums;
std::multiset<int>::iterator l,r;
public:
MedianFinder() {
l=nums.end();
r=nums.end();
}
void addNum(int num) {
nums.insert(num);
int n=nums.size();
if(n==1){
l=r=nums.begin();
}else if(n%2){
//原来是偶数,最后需要指向一个数
if(num>=*l&&num<*r){
l++,r--;
}else if(num<*l){
r--;
}else l++;
}else{
if(num<*l){
l--;
}else r++;
}
}
double findMedian() {
return (*l+*r)/2.0;
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
480. 滑动窗口中位数 - 力扣(LeetCode)
C++ STL prev()和next()函数(深入了解,一文学会)-CSDN博客好文!
cpp
class Solution {
public:
vector<double> medianSlidingWindow(vector<int>& nums, int k) {
std::multiset<int> s(nums.begin(),nums.begin()+k);
int len=nums.size();
int l=0,r=k-1;
auto mid=next(s.begin(),k/2);//从起点往后移动k/2,最后在k/2+1
std::vector<double> ans;
while(r<len)
{
double midd=( (double)(*mid) + (double)(*prev(mid,1-k%2)) ) /2;
ans.push_back(midd);
if(r+1==len) break;
s.insert(nums[++r]);
if(nums[r]<*mid) mid--;
if(nums[l]<=*mid) mid++;
s.erase(s.find(nums[l++]));
}
return ans;
}
};
Problem - C - Codeforces
k次操作,选择i bi=1,ai++ 让max(ai+mid(ci))最大,直接对数组排序,特判中位数前后的中位数
观察样例发现会有特例,特例出现在中位数发生改变的情况,因此二分可能出现的最大中位数。
cpp
#include<bits/stdc++.h>
using ll = long long;
using ull = unsigned long long;
using ari = std::array<int, 3>;
using PII = std::pair<int, int>;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
const double eps = 1e-6;
#define int ll
int n,k;
int c[N];
int tag[N];
struct node{
int a,b;
}d[N];
bool cmp(node a,node b){
return a.a<b.a;
}
void solve() {
std::cin>>n>>k;
for(int i=1;i<=n;i++)
{
std::cin>>d[i].a;
}
//k次,选择i bi=1,ai++
//让max(ai+mid(ci))最大
for(int i=1;i<=n;i++)
{
std::cin>>d[i].b;
}
std::sort(d+1,d+1+n,cmp);
int mid=(n+1)/2;
//1 2 3 122
//1 2 3 4 5
if(n%2){
//删完变偶数
for(int i=1;i<=n;i++)
{
if(i<mid){
c[i]=d[mid].a;
tag[i]=mid;
}else{
c[i]=d[mid-1].a;
tag[i]=mid-1;
}
}
}else{
//1 2 3 4
for(int i=1;i<=n;i++)
{
if(i<=mid){
c[i]=d[mid+1].a;
tag[i]=mid+1;
}else{
c[i]=d[mid].a;
tag[i]=mid;
}
}
}
ll maxn=0;
for(int i=1;i<=n;i++)
{
if(d[i].b!=0)
{
maxn=std::max(maxn,d[i].a+c[i]+k);
}
maxn=std::max(maxn,d[i].a+c[i]);
}
auto check=[&] (int x) ->bool{//判断最大中位数
std::vector<int> v;
int tag;
for(int i=n-1;i>=1;i--)
{
if(d[i].a>=x) v.push_back(i);
else
{
tag=i;
break;
}
}
int num=n-1;
if(v.size()>=num/2+1) return 1;
ll cnt=k;
for(int i=tag;i>=1;i--){
if(d[i].b==0) continue;
int q=x-d[i].a;
if(cnt>=q) {
cnt-=q;
v.push_back(i);
}else break;
}
return v.size()>=(num/2+1);
};
ll l=0,r=INT_MAX,res=-1;
while(l<=r)
{
int mid=l+r>>1;
if(check(mid)){
res=mid;
l=mid+1;
}else r=mid-1;
}
maxn=std::max(maxn,d[n].a+res);
std::cout<<maxn<<'\n';
}
//4 4
//2 1 5 1
//0 1 0 1
//
//1 1 2 5
//1 1 0 0
//2 2 1 1
//1 1 2 5
//2 3 3 5
//5 2
//10 11 4 10 15
//1 1 0 1 0
//
//26
//4 10 10 11 15
//1 1 1 1 0
signed main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
int t = 1;
std::cin>>t;
while (t--) {
solve();
}
return 0;
}