1005 继续(3n+1)猜想(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 个空格隔开,但一行中最后一个数字后没有空格。
输入样例:
6
3 5 6 7 8 11
输出样例:
7 6
由于笔者能力有限,在做这个题的时候走了不少弯路。其实这个题也并不算很难。如果只要求通过,甚至是非常简单的,但是如果要考虑空间的占用率和运行效率的话就必须想的稍多一些。我也看过其他博主写的这个题的答案,其中不少都是虽然简短但是会极大浪费空间的程序。这种做法在数据尚少的情况下倒无所谓,但是数据一旦多了起来那造成的资源浪费就不忍直视了。笔者不才,如有高手莅临,愿闻其教。
分析:
1.输入相应的数据,因为要找出具有一定特征的数并输出,所以用is_key_num与数据共同组成结构体,且初始化 is_key_num的值为1。
2.callatz猜想的思想已给,封装成函数。
3.每个输入数都要计算callatz猜想数,由其定义可知每个输入值得产生的猜想数个数不一定相同,猜想数的存储结构用链表以方便增减数据。
4.每计算一个输入值的callatz猜想值后,用所有猜想值依次与其他所有is_key_num==1的输入元素对比。最终即确定出关键数。
/*总分:25,得分:25*/
#include<stdio.h>
#include<stdlib.h>
#define YES 1
#define NO 0
typedef struct{ //num为输入的数据,is_key_num为关键数的标志位
int num;
int is_key_num;
}data;
typedef struct nd{ //用于存放每个输入数据的Cllatz猜想数,构成链表
int num;
struct nd* next;
}cal_sq_node;
cal_sq_node* callatz(int x); //x:要计算callatz猜想数的值,返回头结点
int main(){
int n;
scanf("%d",&n); //输入数据的个数
if(n<1||n>100)
return(1);
data input_num[n]; //数据将用data形式存储在数组中
for(int i=0;i<n;i++){
scanf("%d",&input_num[i].num);
input_num[i].is_key_num=1; //设定每个输入的数据均为关键数
}
// printf("%d\n",input_num[2].num);//debug:数据输入正确
/* //debug:测试callatz()
cal_sq_node* sq_head=callatz(input_num[0].num);
cal_sq_node* sq_ptr;
for(sq_ptr=sq_head->next;sq_ptr->next!=NULL;sq_ptr=sq_ptr->next){
printf("sq_ptr_add:%p,sq_ptr_num:%d\n",sq_ptr,sq_ptr->num);
}
*/
int num_of_flag_YES=n; //num_of_falg_YES用于表示关键数的个数
int j=0;
for(int k=0;k<n;k++){//计算input_num[].is_key_num为YES的猜想值
if(input_num[k].is_key_num==NO) //此数的猜想值链表必定已经被前面的链表所包含,不必再次计算
continue;
cal_sq_node* cal_sq_head=NULL;
cal_sq_node* cal_sq_ptr=NULL;
cal_sq_head=callatz(input_num[k].num);//得到calltz数的链表头指针
cal_sq_ptr=cal_sq_head;
while(cal_sq_ptr->next!=NULL){
j=0;
// printf("%d\n",cal_sq_ptr->num);
while(j<n){ //若input_num[j].is_key_num==NO则跳过,不用再次比较次数,继续判断下个数
if(input_num[j].is_key_num==YES&&cal_sq_ptr->num==input_num[j].num){
input_num[j].is_key_num=NO;
num_of_flag_YES--;
}
j++;
}
cal_sq_ptr=cal_sq_ptr->next;
}
//用callatz猜想值与其他输入值比较后,将所有此输入值的所有callatz值占用的空间清空
cal_sq_ptr=cal_sq_head;
cal_sq_node* cal_sq_temp;
while(cal_sq_ptr->next!=NULL){
cal_sq_temp=cal_sq_ptr;
cal_sq_ptr=cal_sq_ptr->next;
free(cal_sq_temp);
}
free(cal_sq_ptr);
}
// printf("number of num_of_flag_YES:%d\n",num_of_flag_YES);//debug
int key_num_arr[num_of_flag_YES];
for(int m=0,c=0;m<n;m++){ //将确定为关键数的输入值一起放在数组key_num_arr[]中
if(input_num[m].is_key_num==YES){
key_num_arr[c]=input_num[m].num;
// printf("key_num_arr[%d]:%d\n",c,key_num_arr[c]);
c++;
}
}
//此时的key_num_arr是无序的,排序方案:每个元素均与其后的所有元素对比
for(int k=0;k<num_of_flag_YES;k++){ //对key_num_arr[]从大到小排序并打印
int max_=key_num_arr[k];
int temp;
for(int j=k+1;j<num_of_flag_YES;j++){
if(max_<key_num_arr[j]){
temp=max_;
max_=key_num_arr[j];
key_num_arr[j]=temp;
}
}
if(k!=0)
printf(" %d",max_);
else
printf("%d",max_);
}
}
cal_sq_node* callatz(int x){
cal_sq_node *head_node,*ptr_node;
head_node=(cal_sq_node*)malloc(sizeof(cal_sq_node));
// first_node=(cal_sq_node*)malloc(sizeof(node));
if(!head_node)
exit(1);
head_node->next=NULL;
ptr_node=head_node;
while(x!=1){ //计算猜想值并存放至链表
if(x%2==1)
x=(3*x+1)>>1;
else
x=x>>1;
ptr_node->next=(cal_sq_node*)malloc(sizeof(cal_sq_node));
ptr_node=ptr_node->next;
ptr_node->num=x;
if(x==1)
ptr_node->next=NULL;
}
/* //debug:检查callatz数组成的链表是否准确
for(ptr_node=head_node->next;ptr_node->next!=NULL;ptr_node=ptr_node->next){
printf("ptr_node add:%p,ptr_node num:%d\n",ptr_node,ptr_node->num);
}
*/
return head_node;
}