PAT乙级1030 || 完美数列(C示例解决运行超时)

完美数列

给定一个正整数数列,和正整数 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

解题思路和分析:

题目思路很简单,这题难点在于解决超时问题,

  1. 首先第一点需要进行排序。
  2. 然后遍历数组,按照题目要求求出满足条件的最长数列,这个也简单,稍微难点的是优化遍历方法,稍后介绍。

先展示一下错误示范,也可以直接翻到最后看最终代码:

错误代码示范(C):

        因为我第一次做这道题也是没考虑到各种优化,导致超时了,记录一下我的错误时刻,

#include <stdio.h>
#include <stdlib.h>

int compare(const void * a, const void * b) {
    return * (int *) a - * (int *) b;
}

int main() {
    int N, p;  // 数列长度, 系数
    scanf("%d %d", &N, &p);
    int num[N];  // 存储数列
    int list_max = 0;  // 最大完美数列长度
    
    for (int i = 0;i < N;i++) {  // 循环读取
        scanf("%d", &num[i]);
    } 
    qsort(num, N, sizeof(int), compare);  // 排序

    for (int i = 0;i < N;i++) {
        long min_p = num[i] * p;  // 计算 最小值 * 系数 p
        int num_size = 1;  // 记录数组长度
        for (int j = i + 1;j < N;j++) {
            if ((long)num[j] <= min_p) {
                num_size++;  // 满足最小值 m * p >= 最大值 M, 数列长度 + 1
            }
        }
        if (num_size > list_max) {
            list_max = num_size;  // 更新最大长度
        }
    }
    printf("%d\n", list_max);  // 打印结果
    return 0;
} 

以上代码提交会超时,超时最大原因就是进行了没必要的查询或者计算,所以思路就来了:

  1. 第一:你会发现从数列中计算最大完美数列长度时,从第一个数到最后一个数一直都在循环判断,所以这就是重复计算了。
  2. 第二:以上问题解决之后排序算法也需要用更快的方法,我稍后调整使用的是快速排序。
  3. 第三:其实我还忽略了一点乘法结果超过int的范围,其实我考虑到了,但是没有处理好,后面也有解决。

优化后的代码(C):

        既然说了问题和思路,那我接下来就直接给出我的最终代码:

#include <stdio.h>
#include <stdlib.h>

void quicksort(int *arr,int n)
{
    if(n<2) return;
    int key=arr[0];
    int left=0;
    int right=n-1;
    int which=2;  // 2表示右移动,1表示左移动
    while(left<right)
    {
        if(which==2)
        {
            if(arr[right]>=key) {right--;continue;}
            else if(arr[right]<key)
            {
                arr[left]=arr[right];
                left++;
                which=1;
                continue;
            }
        }
        if(which==1)
        {
            if(arr[left]<=key) {left++;continue;}
            else if(arr[left]>key)
            {
                arr[right]=arr[left];
                right--;
                which=2;
                continue;
            }
        }
    }
    arr[left]=key;
    quicksort(arr,left);  //左递归
    quicksort(arr+left+1,n-left-1);  //右递归
}

int main() {
    int N;
    long p;  // 数列长度, 系数
    scanf("%d %ld", &N, &p);
    int num[N];  // 存储数列
    int list_max = 1;  // 最大完美数列长度,初始化为2是因为在初始计算num_size = list_max - 1

    for (int i = 0;i < N;i++) {  // 循环读取
        scanf("%d", &num[i]);
    } 
    quicksort(num, N);
    for (int i = 0;i < N - list_max;i++) {  // 获得最大长度之后,在数列剩余不足最大长度时最没必要计算        
        for (int j = i + list_max;j < N;j++) {
            if (num[j] <= num[i] * p) {
                list_max++;  // 满足最小值 m * p >= 最大值 M, 数列长度 + 1
            } else {
                break;
            }
        }
    }
    printf("%d\n", list_max);  // 打印结果
    return 0;
} 

以上代码两个大调整:

  1. 使用更高效的排序算法。
  2. 优化了遍历过程,减少不必要的遍历,因为题目只要求求最大完美数列,所以小于最大长度时都可以不必计算,其次我还发现只需要使用到一个记录长度的变量就行,其他都是多余的。

提交代码,答案正确,欢迎大佬指导不恰当的地方。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值