Count Median
编号:0025
题目来源:AtCoder
题目描述
假定有 N N N个整数 X 1 , X 2 , … , X N X_1,X_2,\dotso,X_N X1,X2,…,XN,我们知道其中的任意一个整数 A i ≤ X i ≤ B i A_i\leq X_i \leq B_i Ai≤Xi≤Bi,也就是说我们知道每一个数取值的上下界,要给出这 N N N个整数的中位数的可能取值情况。
当 N & 1 = 1 N \& 1 = 1 N&1=1的时候,也就是 N N N是奇数的时候,中位数是 x N + 1 2 x_{\frac{N+1}{2}} x2N+1
当 N & 1 = 0 N\&1=0 N&1=0的时候,也就是 N N N是偶数的时候,中位数是 ( x N 2 + x N 2 + 1 ) / 2 (x_{\frac{N}{2}} + x_{\frac{N}{2} + 1})/2 (x2N+x2N+1)/2
取值范围:
- 2 ≤ N ≤ 2 × 1 0 5 2\leq N\leq 2\times 10^5 2≤N≤2×105
- 1 ≤ A i ≤ B i ≤ 1 0 9 1\leq A_i \leq B_i\leq 10^9 1≤Ai≤Bi≤109
- 所有输入均为整数
输入
N
A 1 A_1 A1 B 1 B_1 B1
⋮ \vdots ⋮
A N A_N AN B N B_N BN
解答算法
算法思路
很显然,如果一个数 x x x可以做数列 X 1 , ⋯ , X N X_1,\cdots,X_N X1,⋯,XN的中位数,那么起码要有一半的数字可以比 x x x小,那么让所有的数字都取到他们的最小值,然后该数列 A 1 , ⋯ , A N A_1,\cdots,A_N A1,⋯,AN的中位数就应该是整体中位数的最小值;同时也要有一般的数字能够比 x x x大,因此,让所有的数字都取到他们的最大值,然后该数列 B 1 , ⋯ , B N B_1,\cdots,B_N B1,⋯,BN的中位数就是整体中位数的最大值。
当 N N N为奇数的时候,那么中位数的最小变动是1,因此 [ a , b ] [a,b] [a,b]中会有 b − a + 1 b-a+1 b−a+1个可能取值。
当 N N N为偶数的时候,那么中位数的最小变动是 0.5 0.5 0.5,因此 [ a , b ] [a,b] [a,b]中会有 2 ∗ ( b − a ) + 1 2*(b-a)+1 2∗(b−a)+1个可能取值。又因为当 N N N是偶数的时候,a和b都是两个数相加的和,因此可以看作 ( b 1 + b 2 ) − ( a 1 + a 2 ) + 1 (b_1+b_2)-(a_1+a_2)+1 (b1+b2)−(a1+a2)+1
对于求解数列的中位数,比较简单的方法就是直接进行排序,然后去下标中间的数字,这样做的时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn),因为排序的时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)
也可以采用最大最小栈的方法,设定两个优先栈 m a x , m i n max,min max,min,然后每一次输入新的数字,都对两个栈进行替换,这样因为只输入了 n n n个数字,最终时间复杂度应为 O ( n ) O(n) O(n)
代码实现
- 排序实现
#include <bits/stdc++.h> //这个是linux下的头文件,用的VS估计过不了,改一下头文件就好
#include <queue>
using namespace std;
using ull = unsigned long long;
using ll = long long;
int main()
{
int n ;
cin >> n ;
vector<long long> small ; //两个数组,分别用来存放A,B
vector<long long>large ;
for (int i = 0 ; i < n ; ++i) { //输入数组
long long a , b ;
cin >> a >> b ;
small.push_back(a) ;
large.push_back(b) ;
}
sort(small.begin() , small.end()) ; //进行排序
sort(large.begin() , large.end()) ;
if (n % 2 == 1) { //奇偶输出
long long d = large[(n) / 2] - small[(n) / 2] + 1 ;
cout << d << endl ;
}
else {
long long a, b ;
a = small[n / 2 - 1] + small[n / 2 ] ;
b = large[n / 2 - 1] + large[n / 2] ;
cout << b - a + 1 << endl ;
}
return 0;
}
#include <bits/stdc++.h>
#include <queue>
using namespace std;
using ull = unsigned long long;
using ll = long long;
//最大最小栈的读入函数,这个好像是leetcode的一道题,我借用了一下
void addNum(int num, priority_queue<ll, vector<ll>, greater<ll> >& maxheap, priority_queue<ll>& minheap) {
if (maxheap.size() > minheap.size()) {
maxheap.push(num);
minheap.push(maxheap.top());
maxheap.pop();
}
else {
minheap.push(num);
maxheap.push(minheap.top());
minheap.pop();
}
}
int main()
{
priority_queue<ll> minMin, minMax; //设置4个栈
priority_queue<ll, vector<ll>, greater<ll> > maxMin, maxMax;
int N;
scanf("%d", &N);
for(int i = 0; i < N; i++) //依次进行添入
{
ll min, max;
scanf("%lld %lld", &min, &max);
addNum(min, maxMin, minMin);
addNum(max, maxMax, minMax);
}
ll result = 0;
if(N == 1) //判断输出
result = minMax.top() - minMin.top() + 1;
else if(N == 0)
result = 0;
else if(N & 1)
{
ll myMin = maxMin.top(), myMax = maxMax.top();
result = (myMax - myMin + 1);
}
else
result = (minMax.top() + maxMax.top()) - (minMin.top() + maxMin.top()) + 1;
cout << result << endl;
return 0;
}
复杂度分析
- 时间复杂度:一个是 O ( n l o g n ) O(nlogn) O(nlogn),一个是 O ( n ) O(n) O(n)
- 空间复杂度:都为 O ( n ) O(n) O(n)