中位数题解
中位数题解
POJ3579,,DYOJ4218
ssj我以前写过此题的题解了,但后来不小心删掉了
现在又看到这道题,准备重写一下题解
说的不甚清除,甚至可能有些\(小错误\),因为也是《年代久远》的代码了
正文
方法总体如下
- 二分差值(排在中间的差值)
那么,如何去check的这个差值\(M\)是否是*中位数呢
-
将原数组排序,方便后面求差
-
找大于\(M\)的差的个数和小于\(M\)的差的个数
-
如何找,就成为了一个『重要的问题』 😄
以找大于\(M\)的差的个数为例
不妨考虑,从\(i\)(1~n-1)开始,找以\(A[i]\)为被减数的差第一个大于\(M\)的位置
这个位置,是减数的位置,用这个位置与i作差,就是大于\(M\)的差的个数(以\(i\)为被减数的)
找的方法,显然也要二分
因为排序了,所以从h+1到n找减数
代码如下
h: 指上文的i,这里把找的二分封装成了一个函数
m: 指上文的M
int f1(int h, int m)
{
int ans = h, s = h + 1, t = n;
while (s <= t)
{
int k = (s + t) / 2;
if (a[h] - a[k] < m)
ans = k, s = k + 1;
else
t = k - 1;
}
return ans - h;
}
小于也同理
int f2(int h, int m)
{
int ans = n + 1, s = h + 1, t = n;
while (s <= t)
{
int k = (s + t) / 2;
if (a[h] - a[k] > m)
ans = k, t = k - 1;
else
s = k + 1;
}
return n - ans + 1;
}
把所有大于的个数加起来,小于也加起来,看它们是否小于\(C(n,2)/2\)即差的总数/2
小于,说明这个\(M\)是可行的
总代码如下
#include <bits/stdc++.h>
using namespace std;
int n, a[110000], l, r, i, tt, s1, s2;
long long mx = -1, mn = 1000000005;
int f1(int h, int m)
{
int ans = h, s = h + 1, t = n;
while (s <= t)
{
int k = (s + t) / 2;
if (a[h] - a[k] < m)
ans = k, s = k + 1;
else
t = k - 1;
}
return ans - h;
}
int f2(int h, int m)
{
int ans = n + 1, s = h + 1, t = n;
while (s <= t)
{
int k = (s + t) / 2;
if (a[h] - a[k] > m)
ans = k, t = k - 1;
else
s = k + 1;
}
return n - ans + 1;
}
bool cmp(int a, int b)
{
return a > b;
}
int main()
{
while (scanf("%d", &n) == 1)
{
for (i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
if (mx < a[i])
mx = a[i];
if (a[i] < mn)
mn = a[i];
}
l = 0, r = mx - mn, tt = (n - 1) * n / 2;
sort(a + 1, a + 1 + n, cmp);
while (l <= r)
{
int m = (l + r) / 2;
s1 = 0, s2 = 0;
for (i = 1; i < n; i++)
s1 += f1(i, m), s2 += f2(i, m);
if (s1 <= tt / 2 && s2 <= tt / 2)
{
printf("%d\n", m);
break;
}
if (s1 > tt / 2)
r = m - 1;
else
l = m + 1;
}
}
return 0;
}
结束了?
呃,大概也就是这样吧
以后看到有意思的或比赛的题还有\(0.114514\)%会写