1030 完美数列

给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列。

现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列。

输入格式:

输入第一行给出两个正整数 N 和 p,其中 N(≤105)是输入的正整数的个数,p(≤109)是给定的参数。第二行给出 N 个正整数,每个数不超过 109。

输出格式:

在一行中输出最多可以选择多少个数可以用它们组成一个完美数列。

输入样例:

10 8
2 3 20 4 5 1 6 7 8 9

输出样例:

8

    /*
    分析:
    对于一个完美数列,我们只需要知道最大值M和最小值m就可以了,在[m,M]这个范围内
    数一定满足M<=mp,在一个数列中找完美数列,我们对这个数列进行升序排列,之后进行
    操作就会方便一点,排序后:
    1 2 3 4 5 6 7 8 9 20
    这里排序我们还是选用qsort函数,多用用熟悉熟悉;
    以m从左到右开始,找最大值,m=1,M=2,然后M逐个去找;
    那么另外就有M从右到左开始,M=20,m=1,然后m逐个去找;
    还有可能在中间的,这样的话情况就很复杂,再看题目输出,只需要输出最大完美数列
    的个数,那么中间的状态我们可以不关心,我们只需要找下一个数列是否成立,利用已有
    的完美数列最大个数;
    比如:
    1 2 3 4 5 6 7 8 9 20
    p=3时;
    我们用一个maxCount存放最大个数,最后输出,用一个thisCount作为当前的子列里个数;
    maxCount=0;
    这里m=1,让thisCount=1;因为这里左边m已经有了,每多一个我们进行相加;
    M=2;2<=1*3;thisCount++;
    M=3;3<=1*3;thisCount++;
    M=4;4<=1*3不满足,结束m=1的这一段数列;比较maxCount与thisCount
    发现maxCount小于thisCount,于是把thisCount的值赋给maxCount;然后找下一个;
    m=2,maxCount=3,此时我们已经找到一个最大值了,于是小于等于3的值都没有意义了
    因为结果输出的时最大子列的数的个数,小于3不输出,等于3也没必要更改,
    于是M数到第4个
    1 2 3 4 5 6 7 8 9 20
    0 1 2 3 4 5 6 7 8 9
      i 1 2 3 j
    这里m=2,直接让j从第4个开始就可以了
    j==thisCount+i+1==3+1+1==5;这里加1因为我们直接从thisCount的下一个开始
    毕竟相等的时候不需要更新,前提知道m,只有在M之内,都满足M<=m*p;
    M=6,6<=2*3,thisCount++;
    M=7,7<=2*3不满足,更新maxCount,maxCount=thisCount;
    
    然后m等于3,依次循环,直到j碰到数列边界就结束;输出maxCount;
    
    p=4;
    1 2 3 4 5 6 7 8 9 20
    0 1 2 3 4 5 6 7 8 9        //初始:thisCount=0,maxCount=0;
    i j                    //i=0,j=1成立->thisCount=1,maxCount=0;此时实际个数为2;
    i   j                  //i=0,j=2成立->thisCount=2,maxCount=0;此时实际个数为3;
    i     j                //i=0,j=3成立->thisCount=3,maxCount=0;此时实际个数为4;
    i       j              //i=0,j=4成立->thisCount=4,maxCount=0;此时实际个数为5;
    i         j            //i=0,j=5不成立->thisCount=4,maxCount=4;此时实际个数为5;
    
      i       j            //i=1,j=5成立->thsiCount=5,maxCount=4;此时实际个数为6;
      i         j          //i=1,j=6成立->thisCount=6,maxCount=4;此时实际个数为7;
      i           j        //i=1,j=7成立->thisCount=7,maxCount=4;此时实际个数为8;
      i             j      //i=1,j=8不成立->thisCount=7,maxCount=7;此时实际个数为8;
    
        i           j      //i=2,j=8成立->thisCount=7,maxCount=7;此时实际个数为8;
        i             j    //i=2,j=9不成立->thisCount=7,maxCount7;此时实际个数为8;
    
    这里我们发现当上一个i发生不成立时,j不应该从当前位置开始,应该从满足当前i与j之间个数为
    thisCount的位置开始:
    比如:
    当i==0时,在j==5时发生M/m<=p不成立,此时thisCount为4,实际上是5,这里计数没有记录第一个数;
    此时M的下标为1、2、3、4;
    当i==1时,j=thisCount+i=5,但实际应该从j==6的位置开始,下标为2、3、4、5的一共4个;满足thisCount==4
    如果此时j==6成立,那么thisCount++;在j==5的位置成立则会加重复,所以j应该等于i+thisCount+1;
    这里thisCount记录的是最大值M的个数;输出的时候应该加上1;因为最小值m没有统计进来;
    
    注意到第一次开始的时候,j==1==i+thisCount+1成立;
    如果全都不成立,它自身也可以组成数列,自身的数列是一定的,即做最大值也做最小值;
    
    注意这里每个数有可能为     1000000000,int型够用;
    int的范围是-2147483648 - 2147483647
    
    这里我们输入一些特殊数据进行debug
    比如:
    7 2
    4 4 4 4 4 4
    输出为0,然后去改代码;
    */
    #include<iostream>
    #include<cstdlib>        //qsort函数包含在这个函数里;
    //比较函数;
    int compare(const void* x1,const void* x2){
        if(*(int*)x1>=*(int*)x2){
            return 1;
        }
        else {
            return -1;
        }
    }
    using namespace std;
    int main(){
        //输入N和p;
        long long int N,p;
        cin>>N>>p;
        //输入数列;
        long long int data[N];
        for(int i=0;i<N;i++){
            cin>>data[i];
        }
        //排序;
        qsort(data,N,sizeof(long long int),compare);
        //循环找maxCount;
        int thisCount=0,maxCount=0;
        int end=0;    //end为0表示j未到边界,end为1表示j已经到达边界,无需继续判断;
        int j=0;
        for(int i=0;i<N;i++){    //这一层确定m;
            for(int j=i+thisCount;j<N;j++){
                if(data[j]<=p*data[i]){    //M<=m*p转化为M/m<=p;因为乘积可能超出整行范围;
                    thisCount++;
                }
                else{    //这时候就不满足了;//注意如果直到最后一个都满足的话,并没有更改maxCount;
                    if(maxCount<=thisCount){
                        maxCount=thisCount;
                    }
                    break;    //不满足后保存最大值退出;
                }
                if(j==N-1){
                    maxCount=thisCount;    //最后一个也满足,在这里更改maxCount;
                    end=1;
                    break;    //抵达边界;
                }
            }
            if(end==1){
                break;
            }
        }
        //输出最大个数;
        cout<<maxCount<<endl;
        return 0;
    }
/*
反思:
这里第一次是把M<=m*p改为M/m<=p,这样可以用int型存放,并且全是正整数也不会出现除以零的情况;
但是测试点4通不过,答案错误;这里需要总结下除法与乘法之间的问题;
然后就是循环的边界,最开始没有第114行这个赋值语句,忽略了如果最后一个数也成立的话,那么不是
不成立的情况,因此没有更新maxCount的值,以后学代码多考虑各种情况,这里开始就是漏掉了最后一个
数成立的情况;然后就是thisCount的计数方式;之前我统计的是M的个数;也是可以的,因为这里我后面
M开始的位置是从m的位置开始,并非m的下一个,这样计数就是全部的数列数字个数,而非只有最大值;

像这一类题,不需要记录中间状态,只关心最后结果,就不用逐个求中间状态了,第一眼看到这个题,我就想
到最大子列和,和kmp算法这类,可以利用已有的结果优化算法,节省时间;
*/

  • 12
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值