1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > PAT乙级1005 用C语言进行编程 继续卡拉兹猜想

PAT乙级1005 用C语言进行编程 继续卡拉兹猜想

时间:2020-04-07 08:33:41

相关推荐

PAT乙级1005 用C语言进行编程 继续卡拉兹猜想

今天的这道题目着实把我难住了好久,不愧是PAT乙级中值25分的一道题。

这道题呢,是在PAT乙级1001的基础上来增加了一些难度,但是呢,还没有涉及到数据结构,可以说只需要盘清楚逻辑,就可以做这道题了。

我们先来看一看这道题描述了什么:

当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推到的每一个数。

例如对n=3验证的时候,我们需要计算3、5、8、4、2、1,当我们对n=5、8、4、2进行验证的时候,由于我们之前在验证3的时候已经递推过了,所以就不需要验证这些数字。

看到这句话的时候,我还有些不解,但其实很好理解。

我来稍微理一理逻辑:

当n=3的时候,n=3是奇数,根据卡拉兹猜想,奇数要3n+1,然后除以2,很显然可以得到5;

继续往下推,n=5是奇数,(3n+1)/2=8;

偶数是直接除以2,所以8/2=4,4/2=2,2/2=1。

很显然,后面的这些数字5、8、4、2,在我们用卡拉兹猜想验证n=3的时候已经都用到过了,自然而然地也不需要再进行验证了。

审题分析,理清逻辑,流程图

我在每次做这些题目的时候,都会先理逻辑,然后画一个流程图,主要目的呢就是为了帮助我之后如果想来复盘题目的话,能够快速地知道自己哪里出现了问题,并且会较为详细地记录自己的做题过程。

1、第一行给出一个正整数K。

2、第二行给出K个互不相同的待验证的正整数n(1<n<=100)的值,由于要对这些给出的正整数进行数值上的判断,所以最好用数组来进行存储,主要是用来存储一系列相同类型的变量,然后再对数组中的数据来进行处理,需要在for循环中。

3、接下来就是要进行卡拉兹猜想验证,与之前状态相同,就是遍历整个数组,然后从第一个数字开始验证,所以我们要用一个for循环来遍历整个数组。

4、然后,很多数字会在进行卡拉兹猜想验证的时候出现,只要把这些出现的数字与原本数组中的数字进行比较,亦或者直接把原本数组中的这些数字给删除/给这些出现过的数字标上相同的记号,然后再下一次遍历的时候,把这些标过记号的数字不进行输出即可,但是,注意,不能在该数组中进行操作,因为会让该数组的值发生一定的变化,所以需要一个一模一样的数组。

5、数字间用空格隔开,且最后一个数字后面没有空格,先循环输出n-1项,再单独输出最后的第n项。

用一个流程图就能较为直观地看出这道题目的逻辑,接下来就是代码的实现。

代码的实现

这道题呢,对于代码的实现来说,难度可能会有点存在,当然也不像PAT1004一样需要用到一个结构体,正如我之前对这道题目分析的那样,只需要用到数组就行了,一个数组不行那自然可以用两个数组,两个数组不行可以用三个数组,这道题目并没有要求要用到数据结构,所以说,写的稍微复杂些也没什么关系,当然了,代码越简洁越好。

首先,是进行全局变量的定义。

正如我之前对这道题目的逻辑分析讲的那样,我需要一个数组来存储我输入的所有正整数,同时,我也需要一个数组来复制这些正整数,在进行卡拉兹猜想递推的时候,要把这些标记给去除出来,满足这些标记的,就不要显示了(因为已经进行验证过了)。

而不满足这些标记的,才是我们最终需要的。

#include <stdio.h>int main() {int k = 0;//第一行给出的正整数int number[100]; //用来存储输入的这些数int replace[100] = {1}; //用来做标记的数组,假设我们对该数组中的每一个位置做标记为0,那么只要把特定位置的给取出来就行了int count = 0;scanf("%d",&k);//输入正整数kfor(int i=0; i<k; i++){scanf("%d", &number[i]);//输入k个正整数,并用数组的形式来进行存储replace[number[i]] = 1;}//目的是把那些重复的数给剔除,注意,这里的number[i]已经发生了变化,所以不能再用number[i]来作为最终的结果//以下做法满足卡拉兹猜想for(int i=0; i<k; i++){if(number[i]%2==0){number[i] = number[i]/2; //偶数时直接除以2//那么在该位置做上标记,也就是说此位置的replace[i] = 2;replace[number[i]] = 2;}else if(number[i]%2==1){number[i] = (3*number[i]+1)/2; //奇数时为(3n+1)/2replace[number[i]] = 3;}}for(int i=100; i > 0; i--){ //因为我们在平时输出的时候,都是正向输出,所以我们这里只需要反向输出即可if(replace[i] == 1){if(count>0){printf(" ");}printf("%d", i);count++;}}}

该代码在Xcode上面能够完整运行并给出相应的结果:

但是呢,这样却产生了一些问题:我在PAT网站上跑的时候只有15分,却没有拿到剩下的10分,这着实奇怪,对于代码逻辑没有问题,但是最终结果产生了一些问题,那可得抽丝剥茧来进行反思了。

经过我的观察,是发现没有满足这道题的一些特定条件:

1、K要小于100

2、n的范围要大于0小于等于100

顺着这个想法我试着加上了一些条件判断。

但是最终做出来的结果反而没有答案正确了,只剩下部分正确,这就更奇怪了。

else if(number[i]%2==1){number[i] = (3*number[i]+1)/2; //奇数时为(3n+1)/2replace[number[i]] = 3;}

继续进行排查

我发现,这里涉及到一个数组越界的问题了。

由于我定义的初始数组replace[100]范围太小了,是从0开始到99。

而我后面的条件语句判断是100,所以这里就产生问题了。

于是我把replace[i]给调整到replace[1000]了,诶,就发现没什么问题了。

当然,我这里没有对K数值的大小进行判断,不过呢问题也不大,如果要对K数值的大小进行判断的话,只需要在第一个for循环之前加一个if(K<100)的条件语句即可。

#include <stdio.h>int main() {int k = 0;//第一行给出的正整数(<100)int number[1000]; //用来存储输入的这些数, 0< n = number <=100int replace[1000] = {1}; //用来做标记的数组,假设我们对该数组中的每一个位置做标记为0,那么只要把特定位置的给取出来就行了int count = 0;scanf("%d",&k);//输入正整数kfor(int i=0; i<k; i++){scanf("%d", &number[i]);//输入k个正整数,并用数组的形式来进行存储if(number[i]>1&&number[i]<=100){replace[number[i]] = 1;}}//目的是把那些重复的数给剔除,注意,这里的number[i]已经发生了变化,所以不能再用number[i]来作为最终的结果//以下做法满足卡拉兹猜想for(int i=0; i<k; i++){while(number[i]!=1){if(number[i]%2==0){number[i] = number[i]/2; //偶数时直接除以2//那么在该位置做上标记,也就是说此位置的replace[i] = 2;if(number[i]<=100){replace[number[i]] = 2; //给这些已经递推过的数标上2}}else if(number[i]%2==1){number[i] = (3*number[i]+1)/2; //奇数时为(3n+1)/2if(number[i]<=100){replace[number[i]] = 3; //给这些已经递推过的数标上3}}}}for(int i=100; i > 0; i--){ //因为我们在平时输出的时候,都是正向输出,所以我们这里只需要反向输出即可if(replace[i] == 1){if(count>0){printf(" ");}printf("%d", i);count++;}}}

最后的测试结果:

总结

这道题目难度的确有一点,我自己做了好长时间,而且也查阅了资料才做出来的,总的来说主要是因为我对C语言的确不是很熟悉,所以得好好反思,再继续努力才行!!!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。