[PAT Basic Level] 1005.继续(3n+1)猜想

题目分析

这道题乍看起来比较复杂,不容易想到什么比较好的应对策略,至少我没有想到。 所以没有办法,朴素的思想蛮力硬干。

目标是找出那些“关键数”,稍加分析可以知道,这实际上就是指删除那些会出现在其他数的卡拉兹序列(这里称一个数按卡拉兹猜想的规则进行运算,一直到1所产生的序列)的数。

因此就可以想出这样一种办法:
从第一个数出发,求出它的卡拉兹序列,然后对整个数组除当前数外的所有元素进行遍历,找出所有出现在卡拉兹序列中的数,删除(同时也“降低”了后面的搜索规模)。之后再移到下一个数,重复前面的工作,循环直到遍历完数组。

可以看到这样需要频繁“删除”数组中的元素,那真用数组的话势必需要频繁移位,显然这样很复杂,用链表似乎也有些浪费。所以一种替代方案是另外创建一个bool型数组来表征对应的数组中元素是否还有效,若被删除则将对应序号的bool值修改为false即可。

在计算卡拉兹序列时,由于长度未知,又不想开太大的数组浪费空间,可以采用动态扩容的手段。

最后遍历完“删除”掉所有的非关键元素,题目还要求我们按从大到小的顺序输出,于是还需要遍历一遍数组,找出那些尚存的关键元素,另用一个数组保存,这里为了方便,由于题目已经告诉我们不会超过100个元素,所以直接开了大小为100的int数组来保存,之后再排序一遍输出即可。

优化策略
再多注意一下题目的条件,可以发现题目告诉我们元素大小不会超过100,那么我们之前每次计算的卡拉兹序列中也不用保存超过100的元素,这样可以缩小数组大小,减少遍历和比较时间。

整个题目按这个思路干下来,还是有些繁琐,最后排序为了简单少写点代码就用了插入排序。不过相比于前面比较和删除元素的复杂度,排序剩下那几个元素的时间几乎可以忽略不计了,所以不用写性能很好的排序算法,简单正确就行。

源代码

#include <stdio.h>

const int DEFAULT_SIZE=30;   //设置储存卡拉兹序列数组默认大小为30,满后再进行扩容
void expand(int *&arr,int len);  //对数组扩容
int* findPath(int num,int &len);  //计算卡拉兹猜想过程数
void insertSort(int *arr,int len);   //插入排序

int main()
{
    int caseNumber;
    scanf("%d",&caseNumber);
    getchar();
    int *value=new int[caseNumber];  //储存元素值
    bool *isValid=new bool[caseNumber];  //判断元素是否还有效
    for(int i=0;i<caseNumber;++i){  //读取数据
        scanf("%d",&value[i]);
        isValid[i]=true;  //初始化为true
    }
  
    for(int i=0;i<caseNumber;++i){
        if(!isValid[i]) continue;
        int len=0;
        int* path=findPath(value[i],len);  //计算当前有效元素的卡拉兹路径元素
        for(int j=0;j<caseNumber;++j){  //对除当前元素外所有元素进行遍历,删除被覆盖元素
            if(!isValid[j]||j==i) continue;
            for(int k=0;k<len;++k){
                if(path[k]==value[j]){  //发现路径上的元素,删除
                    isValid[j]=false; 
                    break;
                }
            }
        }
       delete []path;  //释放当前路径数组的空间
    }
    int keys[100];  //剩余数个数不超过100
    int count=0;
    for(int i=0;i<caseNumber;++i){
        if(!isValid[i]) continue;
        keys[count++]=value[i];  //记录剩下的关键数
    }
    insertSort(keys,count);
    for(int i=0;i<count-1;++i)
        printf("%d ",keys[i]);
    printf("%d",keys[count-1]);
    delete []value;
    delete []isValid;
    return 0;
}

void expand(int *&arr,int len)
{
    int *old=arr;
    arr=new int[len*2];  //扩容为原来两倍
    for(int i=0;i<len;++i)
        arr[i]=old[i];
    delete []old;   //释放原空间
}
int* findPath(int num,int &len)
{
    len=1;
    int curSize=1;
    int capacity=DEFAULT_SIZE;
    int * path=new int [capacity];
    path[0]=num;  
    while(num!=1){
        len++;
        if(num%2==0)  num>>=1;
        else num=(3*num+1)>>1;
        if(num<101) path[curSize++]=num;  //仅当所得数值小于101时放入数组
        if(curSize==capacity){  //若数组满,扩容
            capacity<<=1;
            expand(path,curSize);
        }
    }
    return path;
}
void insertSort(int *arr,int len)
{
    for(int i=1;i<len;++i){
        int curOrder=i;
        int pre=i-1;
        while(arr[curOrder]>arr[pre]){
            int tmp=arr[curOrder];
            arr[curOrder--]=arr[pre];
            arr[pre--]=tmp;
            if(curOrder==0) break;
        }
    }
}
发布了11 篇原创文章 · 获赞 1 · 访问量 74

猜你喜欢

转载自blog.csdn.net/weixin_44452361/article/details/104602637