百度之星 初赛roundB 1006 中位数计数 详解

来源:互联网 发布:知乎和百度问问的区别 编辑:程序博客网 时间:2024/04/30 08:50

这道题实在是不适合我这种蠢的要死的人来做的,我花了一个星期才想懂,在acm的道路上毕竟不容易啊


题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5701


这道题是说给出一列数字,要求求出每个数字能在多少个区间内成为中位数。比如给出的1 2 3 4 5,分析一下3,3在1 2 3这个区间不是中位数,在4 5 6这个区间也不是中位数,只在2 3 4 和1 2 3 4 5是中位数,每个数都要这样判断,看起来就很头疼啊。

中位数是啥?说白了就是在这个区间里面,小于这个选定数字key的数字个数为a,大于的有b个,a==b,那么key在这个区间就是中位数了。所以我们有这样的方法。

选定第i个数key,在 for(j  =  i - 1 -> 0)走一遍,初始化一个t = 0,如果a [j] > key,就让 t++,反正就 t--,然后再让t = 0,同时对for(j  =  i + 1 -> n - 1)这样对于1 2 3 4 5来说,就会得到一个数组,-2 -1 ? 1 2,这样可以很直观看到,1 + (-1)  和 2 + (-2)  ==  0,就说明了这两个区间他都是中位数,再加上他自己[2,2]是中位数,输出3就行了。

我们再对一个随机点的数组去讨论,1 3 4 2 5,依然选取3,就会得到-1 ? 1 0 1,我们可以看到,有两个1 和 一个 -1去匹配,输出3。对于4,就会得到-2 -1 ? -1 0,所以输出2。

但是这样的算法的话,我们需要在判断 [i + 1 , n - 1]时候,每次都要在 [0,i - 1]遍历一次,这样的时间复杂度我们来算算,就是

for(i=0;i<n;i++)//选定第i个数进行分析 {for(j=i-1;j>=0;i--)//先对前面的进行计算,得到一个数组 for(j=i+1;j<n;i++){for(j=i-1;j>=0;i--) //这里进行遍历 }}  
都是要按照最大值来计算的,就是O(n^4)(我也不知道对不对。。。),题目所给的最大数据是8000个是吧,8000*8000=64000000,六千四百万,计算机尚能接受,但是再平方一次就比较尴尬了。。。(这种也是很暴力相当于对于每个选定的数字key把整个序列都走一边,那个好像还好点,只需要O(n^3))

所以我们需要去发现一些东西去优化他(本小白在这卡了N久)。

1,在[i + 1 , n - 1]这个区间里面,我们还需要存进去数组里面吗?直接判断就可以了。

2,我们有没有办法去把前面这些正负去放在一个有规律的区间里面,方便我们后面遍历的时候直接就可以一一对应找到这个数字呢?

       比如  7 6 5 2 3 4 1 5里面的4,前面得到的是1 0 -1 -2 -1,我们存储这些数据的时候加个if去判断,正数或者放在pos数组里面,负数放在neg数组里面,而且要有规律的话,正数就让pos[temp]++,负数就让neg[-temp]++,用这个来说明正数1,2,3这些,负数-1,-2,-3这些有多少个,这样有个什么好处呢?我们刚才讨论的那种情况,如果遇到了

-1 0 -1 0 -1 ? 1 0 1 0 1这样,那每个1都要去遍历一次左边有多少个-1,网上有句话所的好,你484傻。每个1直接找到前面-1的个数,也就是neg[1],那就是很happy了吗?这样就导致了我不需要去遍历前面的区间了,直接在[i + 1 , n - 1]一边t++,t--,一边看他的neg,pos对应的元素不就可以了吗?这就变成了

for(i=0;i<n;i++){for(j=i-1;i>=0;i--)for(j=i+1;i<n;i++)}   
这就相当于n*n了啊,电脑也表示你终于不用虐待我了(虽然我会罢工)。

代码如下:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int main(){int i,j,n,a[8005],pos[8005],neg[8005],cnt,t;while(scanf("%d",&n)!=EOF){for(i=1;i<=n;i++){scanf("%d",&a[i]);}for(i=1;i<=n;i++){memset(pos,0,sizeof(pos));//正数 memset(neg,0,sizeof(neg));//负数 pos[0]=1;//自己所在区间是一个中位数 if(i!=1)printf(" ");for(j=i-1,t=0;j>0;j--){if(a[j]<a[i])--t;else++t;if(t>=0)pos[t]++;elseneg[-t]++;}for(j=i+1,t=0,cnt=pos[0];j<=n;j++)//cnt=pos[0]很重要,在1到i-1区间内只要有0就可以直接构成中位数了 {if(a[j]<a[i])--t;else++t;if(t>0)cnt+=neg[t];//反过来,正数要找负数,负数要找正数 elsecnt+=pos[-t];}printf("%d",cnt);}printf("\n");}return 0;} 


0 0
原创粉丝点击