经过了几天和算法笔记&codeup的大战,我又回来啦,这次是25分题~
一、题目信息
卡拉兹(Callatz)猜想已经在1001中给出了描述。在这个题目里,情况稍微有些复杂。
当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数。例如对n=3进行验证的时候,我们需要计算3、5、8、4、2、1,则当我们对n=5、8、4、2进行验证的时候,就可以直接判定卡拉兹猜想的真伪,而不需要重复计算,因为这4个数已经在验证3的时候遇到过了,我们称5、8、4、2是被3“覆盖”的数。我们称一个数列中的某个数n为“关键数”,如果n不能被数列中的其他数字所覆盖。
现在给定一系列待验证的数字,我们只需要验证其中的几个关键数,就可以不必再重复验证余下的数字。你的任务就是找出这些关键数字,并按从大到小的顺序输出它们。
输入格式:每个测试输入包含1个测试用例,第1行给出一个正整数K(<100),第2行给出K个互不相同的待验证的正整数n(1<n<=100)的值,数字间用空格隔开。
输出格式:每个测试用例的输出占一行,按从大到小的顺序输出关键数字。数字间用1个空格隔开,但一行中最后一个数字后没有空格。
二、基本思路
这题的前提条件是B10001中的卡拉兹猜想,具体不赘述。
我做这题的基本思路是:对于每一个需要验证数,根据猜想的计算方法进行1次后,和剩余数进行比较(进行1次后比较可以删去自己覆盖自己的情况),如果发现相等值,则代表发现“非关键数”,则在对应的数组B的对应序号B[i]处,将值赋为1;
一轮下来,所有“非关键数”均被标记,剩余的位置即为“关键数”,随后将关键数存入数组C中;
之后对C中的数,根据冒泡法排序,最后打印输出即可。
三、具体实现代码
1、第一次提交
#include<stdio.h>
#include<string.h>
int main()
{
int K,A[100],i,B[100],N,j,C[100],k=0,saleman;/*K代表数字个数,A存储待验证的数,B存储每个数被覆盖的次数*/
memset(B,0,100); /*B先全部赋0*/
scanf("%d", &K);
for(i=0;i<K;i++) /*待验证的数*/
scanf("%d",&A[i]);
for(i=0;i<K;i++) /*从第一个数开始认证起*/
{
N = A[i];
while(N!=1)
{
if(N%2==1) /*N为奇数*/
N = (3*N+1)/2;
else /*N为偶数*/
N /= 2;
for(j=0;j<K;j++) /*从第一个开始,统计除自己之外的重合*/
{
if(A[j]==N)
{
B[j] = 1; /*重了就标记*/
}
}
}
}
/*循环结束后,所有被覆盖的值均被标记,将不被覆盖的值存入C*/
for(i=0;i<K;i++)
{
/* printf("B[%d]的值为%d\n",i,B[i]);*/
if(B[i]==0) /*说明没被覆盖*/
{
C[k] = A[i];
k++;
/* printf("这是一个未覆盖数%d\n",C[k-1]); */
}
}
/*存储完成之后,k的值即为未覆盖的值的量,然后进行冒泡法排序*/
for(i=0;i<k;i++)
{
for(j=i+1;j<k;j++) /*和所有后面的数比较大小,将最大的数换到前排*/
{
if(C[j]>C[i])
{
/* printf("A[%d]=%d小于A[%d]=%d,交换\n",i,A[i],j,A[j]);*/
saleman = C[j];
C[j] = C[i];
C[i] = saleman;
}
}
}
/*换序完成,打印输出即可*/
for(i=0;i<k;i++)
{
if(i==0)
printf("%d",C[i]);
else
printf(" %d",C[i]);
}
printf("\n");/*回车,收工*/
}
其中有一部分备注为测试代码。
评测结果
时间 | 结果 | 得分 | 题目 | 语言 | 用时(ms) | 内存(kB) | 用户 |
---|---|---|---|---|---|---|---|
5月03日 17:36 | 部分正确 | 22 | 1005 | C (gcc 4.7.2) | 1 | 264 | chauncyyoung |
测试点
测试点 | 结果 | 用时(ms) | 内存(kB) | 得分/满分 |
---|---|---|---|---|
0 | 答案正确 | 1 | 264 | 15/15 |
1 | 答案正确 | 1 | 256 | 2/2 |
2 | 答案正确 | 1 | 264 | 2/2 |
3 | 答案正确 | 1 | 264 | 3/3 |
4 | 答案错误 | 1 | 256 | 0/3 |
测试点4出现错误..
.然后开始了漫长的找错之旅...
可能错误1:数组大小
问度娘之后,有道友指出应该将数组大小设置为101,于是试了一下,不对...
可能错误2:重复判断冗余
题目规定没有重复的数,所以发现一个相同,循环就可以跳出了
在那段循环中加了break,测试分数不变...
可能错误3:另一个角度的重复判断冗余
这个角度有点刁钻。以3和5为例:3第一次运算能发现将5覆盖,5之后的运算绝不可能再出现3。所以可以直接将被覆盖数归0不考虑。
for(i=0;i<K;i++) /*从第一个数开始认证起*/
{
N = A[i];
if(N==0)
continue;
while(N!=1)
{
if(N%2==1) /*N为奇数*/
N = (3*N+1)/2;
else /*N为偶数*/
N /= 2;
for(j=0;j<K;j++) /*从第一个开始,统计除自己之外的重合*/
{
if(A[j]==N)
{
A[j] = 0; /*重了就标记*/
break;
}
}
}
}
/*循环结束后,所有被覆盖的值均被标记,将不被覆盖的值存入C*/
for(i=0;i<K;i++)
{
/* printf("B[%d]的值为%d\n",i,B[i]);*/
if(A[i]!=0) /*说明没被覆盖*/
{
C[k] = A[i];
k++;
/* printf("这是一个未覆盖数%d\n",C[k-1]); */
}
}
重点修改了这一段,因为直接将原数组元素归0,所以不需要用到B了。
评测结果
时间 | 结果 | 得分 | 题目 | 语言 | 用时(ms) | 内存(kB) | 用户 |
---|---|---|---|---|---|---|---|
5月03日 18:08 | 答案正确 | 25 | 1005 | C (gcc 4.7.2) | 1 | 264 | chauncyyoung |
测试点
测试点 | 结果 | 用时(ms) | 内存(kB) | 得分/满分 |
---|---|---|---|---|
0 | 答案正确 | 1 | 264 | 15/15 |
1 | 答案正确 | 1 | 264 | 2/2 |
2 | 答案正确 | 1 | 256 | 2/2 |
3 | 答案正确 | 1 | 256 | 3/3 |
4 | 答案正确 | 1 | 264 | 3/3 |
早知道是这样,如梦一场~~
最后附上完整修改版代码
#include<stdio.h>
#include<string.h>
int main()
{
int K,A[100],i,N,j,C[100],k=0,saleman;/*K代表数字个数,A存储待验证的数,B存储每个数被覆盖的次数*/
scanf("%d", &K);
for(i=0;i<K;i++) /*待验证的数*/
scanf("%d",&A[i]);
for(i=0;i<K;i++) /*从第一个数开始认证起*/
{
N = A[i];
if(N==0)
continue;
while(N!=1)
{
if(N%2==1) /*N为奇数*/
N = (3*N+1)/2;
else /*N为偶数*/
N /= 2;
for(j=0;j<K;j++) /*从第一个开始,统计除自己之外的重合*/
{
if(A[j]==N)
{
A[j] = 0; /*重了就标记*/
break;
}
}
}
}
/*循环结束后,所有被覆盖的值均被标记,将不被覆盖的值存入C*/
for(i=0;i<K;i++)
{
/* printf("B[%d]的值为%d\n",i,B[i]);*/
if(A[i]!=0) /*说明没被覆盖*/
{
C[k] = A[i];
k++;
/* printf("这是一个未覆盖数%d\n",C[k-1]); */
}
}
/*存储完成之后,k的值即为未覆盖的值的量,然后进行冒泡法排序*/
for(i=0;i<k;i++)
{
for(j=i+1;j<k;j++) /*和所有后面的数比较大小,将最大的数换到前排*/
{
if(C[j]>C[i])
{
/* printf("A[%d]=%d小于A[%d]=%d,交换\n",i,A[i],j,A[j]);*/
saleman = C[j];
C[j] = C[i];
C[i] = saleman;
}
}
}
/*换序完成,打印输出即可*/
for(i=0;i<k;i++)
{
if(i==0)
printf("%d",C[i]);
else
printf(" %d",C[i]);
}
printf("\n");/*回车,收工*/
}