中位数计数
Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1401 Accepted Submission(s): 521
Problem Description
中位数定义为所有值从小到大排序后排在正中间的那个数,如果值有偶数个,通常取最中间的两个数值的平均数作为中位数。
现在有 n 个数,每个数都是独一无二的,求出每个数在多少个包含其的区间中是中位数。
现在有 n 个数,每个数都是独一无二的,求出每个数在多少个包含其的区间中是中位数。
Input
多组测试数据
第一行一个数 n(n≤8000)
第二行 n 个数, 0≤ 每个数 ≤109 ,
第一行一个数 n(n≤8000)
第二行 n 个数, 0≤ 每个数 ≤109 ,
Output
N
个数,依次表示第
i
个数在多少包含其的区间中是中位数。
Sample Input
5 1 2 3 4 5
Sample Output
1 2 3 2 1
Source
为什么看了题解之后感觉明明可以想到的啊,可是自己想了好久还是做不出来,方法不对啊。。。,看到这个题没有直接往这里想。。。希望以后可以再成长一些吧
思路:肯定不可以两个for枚举区间在每个区间里找到中间大的数字,绝壁超时 ,既然是中位数,也就是这个数比他大的数 == 比他小的数,然而包含一个数的区间怎么找呢,无非就是这个数为右边界,为左边界,在中间。用一个cnt记录这个区间里总的大小,比他大就++,比他小就--,如果==0说明这个就是中位数(明明用过好多次为什么就是想不到),先以这个点往左枚举, == 0,说明在这个区间是中位数,再以这个点往右枚举, == 0说明是中位数,但是注意之所以要先这样枚举,是找可以直接找到这个数在区间中间的个数,只要左面+右面的cnt == 0,说明这两个区间拼在一起可以符合要求,(这里的cnt是“总的”的值,>他的 < 去小于他的)。 然后就是组合左面跟右面的区间,只要他们的值相同,就可以组合在一起。。
一般的写法:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 8e3;
int a[maxn], cnt1[maxn<<1], cnt2[maxn<<1];
int main()
{
int n;
while(~scanf("%d", &n))
{
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(int i = 1; i <= n; i++)
{
memset(cnt1, 0, sizeof(cnt1));
memset(cnt2, 0, sizeof(cnt2));
int ans = 1, cnt = 0;
for(int j = i - 1; j >= 1; j--)
{
cnt += a[i] > a[j] ? 1 : -1;
if(!cnt) ans++; //这里的ans++指的是不用左右区间组合的情况下可以是中位数的
cnt1[n+cnt]++; //这里记录 总的“比他小的” 用比他小大-比他大的
}
cnt = 0;
for(int j = i + 1; j <= n; j++)
{
cnt += a[i] < a[j] ? 1 : -1; //这里记录的总的“比他大的”,只要左右比他大==比他小就可以组合在一起
if(!cnt) ans++; //这里是单一右面区间可以为中位数的值
cnt2[n+cnt]++;
}
for(int i = 0; i <= n*2; i++) //这里就是左右区间组合,数值多大,说明有几种不同的长度的区间,组合方式肯定是左面*右面的;
ans += cnt1[i]*cnt2[i];
printf("%d%c", ans, i == n ? '\n' : ' ');
}
}
return 0;
}
网上大牛的写法,写的真是妙,时间也快了一倍:
#include <cstdio>
#include <cstring>
int const MAX = 8005;
int const CON = 8000;
int a[MAX], has[MAX << 1];
int main()
{
int n;
while(scanf("%d", &n) != EOF)
{
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(int i = 1; i <= n; i++)
{
int cnt = 0, ans = 0;
memset(has, 0, sizeof(has));
has[CON] ++;
for(int j = i + 1; j <= n; j++)
{
cnt += a[j] > a[i] ? 1 : -1;
has[CON + cnt] ++;
}
cnt = 0;
ans += has[CON]; //这里就是单一区间是0的;
for(int j = i - 1; j >= 1; j--)
{
cnt += a[j] < a[i] ? 1 : -1;
ans += has[CON + cnt]; //遇到一个相同的,就++右面区间的数值,先直接枚举一个区间,然后中间的跟左面都就求出来了、、
}
printf("%d%c", ans, i == n? '\n' : ' ');
}
}
}