爆刷PAT(甲级)——之【1029】 Median(25 分)——队列问题+内存优化

版权声明:本文为博主原创文章,未经博主允许不得转载。(但可以打钱或卖萌.jpg) https://blog.csdn.net/Hide_in_Code/article/details/81986596

PS:如果你看完了下面的内容,那很不幸,很多都是没必要的。。。因为我才发现,long int 就是int !(可恶

也就是说下面关于内存的什么long long 好像都是我YY出来的,因为数据类型题目已经保证了是int ...... 惭愧惭愧,博客也懒得改了,以下为原答案:


题意:给两个有序序列,输出两个序列合并以后的中位数。

难点:我很惭愧,这题没有用到高深的算法,没有用到优秀的数据结构,没有扯到玄学的数学知识,没有出现脑残的题意不清。然鹅,我就是没A。能力着实不足呀。

此题N小于2×10^5,本题最大的难度不是时间问题。而是空间!

1、本题我在考研复习的时候在数据结构的书上看到类似的,使用一种类似二分的思想,可以在O(logn)的时间内实现,而不是O(n)。但是后来发现,那题虽然和本题一样,都是给了两个有序的序列输出中位数,但是本题的两个序列是不定长的!使得问题复杂化,不能使用二分了。

2、最重要的一点,在提交完O(n)的代码之后,“内存溢出”的错误提示,我才明白本题的卡内存才是难点。也就是说不能存储两个long long  的数组,否则就会内存溢出。

3、于是我只存储第一个序列,采用第二个序列只读第一个数的办法,这样理论上内存节省了一半,应该就不会内存溢出了。然鹅,这样写的程序,要兼顾两个序列不定长带来的,对于“哪个队列更短导致的队列先空队列”的边界讨论,大大影响的代码的书写。导致我程序错误的同时,内存依然溢出了!

4、在挣扎了一个晚上之后,学习了柳神的代码,最后发现了她的代码与解题思路更加优化,最后借鉴了柳神的代码才AC。。。柳神太厉害了。。。(别人的代码都已经过不了了,因为18年3月份之后PAT对此题进行了内存溢出的强化样例,直接匹配或者是队列存储都会溢出的!)

在此总结一下,本题我(借鉴学习之后)的思路:

1)本题的内存限制是1.5MB,输入一个序列最长2×10^5个数,每个数都是 long int 型的;而如果假设所有数字都是int型的,那么每个数的存储是4B,那么一个序列最长的情况下就要占据0.8MB的内存。如果是long int,那就直接1.6MB了。这么计算之后,我才明白为什么柳神说:“本题的long int中的数据不可能是答案,都是干扰数据。”

题目的AC思路,就是输出两个序列的更小的那项,输出前1/2总数的项数,下一个就是中位数了

而对于序列存储的处理办法是,开队列的时候,就开 int型的队列,输入的数据,大于int就用int的最大值去代替,这样就可以存下第一个序列了

2)第二个序列,用int型存也会内存溢出,柳神的办法很有趣——每一次读取第二个序列的输入数的之后,都进行一次比较,那么要不就是序列1的队列长度减一,要么就是序列2的队列长度减一,使得总程序的内存占用没有增加!这样就可以使得内存不会溢出了!

而如果是把第二个序列全存下来是不现实的;如果只读入第二个序列的第一个数字,只在第二个序列小的时候再读入第二个序列的下一个数字,这样书写代码逻辑的时候又很烦,所以总的来说,柳神这个写法更好诶。

3)考虑一下边界,我是把队空判断逻辑放在总的比较循环体中了,所以要考虑两个队列谁空的情况即可。

 

Code:

上代码,和优秀还是差得很远啊。

#include<bits/stdc++.h>
using namespace std;
#define inf 200009
#define INF 0x3f3f3f3f
#define loop(x,y,z) for(x=y;x<z;x++)
#define ll long long

queue<int>a,b;
int len1,len2;

void debug(int i,char *s)//debug输出用
{
    printf("debug: %d %s\n",i,s);
}

void Input()
{
    int i,cnt;
    ll t;
    scanf("%d",&len1);
    loop(i,0,len1)
    {
        scanf("%lld",&t);
        t=min(t,(ll)INT_MAX);
        a.push(t);
    }

    scanf("%d",&len2);

    int sum=len1+len2;
    if(sum%2==0)sum=sum/2-1;
    else sum/=2;//计算出要吐掉多少个

    i=cnt=0;
    while(1)
    {
        if(i<len2)
        {
            scanf("%lld",&t);
            t=min(t,(ll)INT_MAX);
            b.push(t);
            i++;
        }
        if(cnt==sum)break;

        if(a.empty())//有一方队列空,边界
        {
            b.pop();//debug(1,"a empty");
            cnt++;
            continue;
        }
        else if(b.empty())
        {
            a.pop();//debug(2,"b empty");
            cnt++;
            continue;
        }
        if(a.front()<b.front())
            a.pop();//debug(3,"a small");
        else
            b.pop();//debug(4,"b small");
        cnt++;
    }
    if(a.empty())printf("%d\n",b.front());
    else if(b.empty())printf("%d\n",a.front());
    else printf("%d\n",min(a.front(),b.front()));
}
int main()
{
    Input();
    return 0;
}

 

展开阅读全文

没有更多推荐了,返回首页