数字黑洞求解问题
任意一个5位数,比如:34256,把它的各位数字打乱,重新排列,可以得到一个最大的数:65432,一个最小的数23456。求这两个数字的差,得:41976,把这个数字再次重复上述过程(如果不足5位,则前边补0)。如此往复,数字会落入某个循环圈(称为数字黑洞)。
比如,刚才的数字会落入:[82962, 75933, 63954, 61974] 这个循环圈。要求编写程序,找到5位数所有可能的循环圈,并输出。
这是某个比赛中的一个问题,我试着用c语言求了一下。大概思路就是:把五位数拆分,排序,求最大和最小数,求差,再判断是否落入了黑洞数的循环。
/**************************************** 输出5位数的全部黑洞数 *****************************************/ #include<stdio.h> int Max_Min(int num); void Find(int num); int c[20]={0},k=0; //这两个全局变量是为了查重 void main() { int num; for(num=10000;num<99999;num++) { Find(num); } } /********************************** 函数名称:Max_Min 函数功能:把一个五位数的各位提取出来, 重新组合成最大数和最小数,并求差 参数:num--输入的五位数 返回值:sub_result--最大数与最小数的差 ***********************************/ int Max_Min(int num) { char i=0,j=0,t; int max=0,min=0,sub_result; char a[5]; for(i=0;i<5;i++) //把各位数取出来,不足5位的补0 { a[i]=num%10; num=num/10; } for(i=0;i<=3;i++) //从大到小排序 for(j=0;j<=3-i;j++) { if(a[j]<a[j+1]) { t=a[j]; a[j]=a[j+1]; a[j+1]=t; } } for(i=0;i<=4;i++) { max=max*10+a[i]; //组成的最大的数 } for(i=4;i>=0;i--) { min=min*10+a[i]; //组成的最小的数 } sub_result=max-min; //求差 return sub_result; } /********************************* 函数名称:Find 函数功能:判断输入的数是否有黑洞数,并输出 参数:num--输入的五位数 **********************************/ void Find(int num) { char i,j,m,flag=0; int b[100]; b[0]=Max_Min(num); for(i=1;i<100;i++) //测试前100个 { b[i]=Max_Min(b[i-1]); for(j=0;j<i;j++) { if(b[j]==b[i]) { flag=1; for(m=0;m<20;m++) //查看前面是否已经输出过 { if(b[j]==c[m]) { flag=0; break; } } break; } } if(flag==1) //存在黑洞数并且前面没有输出过 { printf("%c",'['); for(;j<i-1;j++) { printf("%d%c",b[j],','); c[k++]=b[j]; } printf("%d",b[j]); c[k++]=b[j]; printf("%c",']'); printf("\n"); return ; } } }
这段程序很多不足之处,比如在对最大和最小数求差的时候,只求了100次,当然算出来结果也是对的,因为五位数的黑洞数都能在100次之内被检测出来,其实实际情况远远用不了100次。结果虽然对,但思路总归不严谨。下面给出一个用递归法求解的。
#include<stdio.h> int Max_Min(int num); void Find(int num); int record1[50]={0},k=0; //这两个全局变量是为了查重 int record2[100],f; //每次使用前清零 void main() { int num; char m; for(num=10000;num<=99999;num++) { Find(num); for(m=0;m<100;m++) //由于是全局变量,在本次使用后清零 { record2[m]=0; } f=0; } } /********************************** 函数名称:Max_Min 函数功能:把一个五位数的各位提取出来, 重新组合成最大数和最小数,并求差 参数:num--输入的五位数 返回值:sub_result--最大数与最小数的差 ***********************************/ int Max_Min(int num) { char i=0,j=0,t; int max=0,min=0,sub_result; char a[5]; for(i=0;i<5;i++) //把各位数取出来,不足5位的补0 { a[i]=num%10; num=num/10; } for(i=0;i<=3;i++) //从大到小排序,冒泡法排序 for(j=0;j<=3-i;j++) { if(a[j]<a[j+1]) { t=a[j]; a[j]=a[j+1]; a[j+1]=t; } } for(i=0;i<=4;i++) { max=max*10+a[i]; //组成的最大的数 } for(i=4;i>=0;i--) { min=min*10+a[i]; //组成的最小的数 } sub_result=max-min; //求差 return sub_result; } /********************************* 函数名称:Find 函数功能:判断输入的数是否有黑洞数,并输出 参数:num--输入的五位数 **********************************/ void Find(int num) { char i,j,m; int n; n=Max_Min(num); for(i=0;i<f;i++) { if(n==record2[i]) //检测黑洞数 { for(j=0;j<50;j++) //检测是否已经输出过 { if(n==record1[j]) { return; //前面已经输出过,退出函数 } } printf("%c",'['); for(j=i;j<f-1;j++) //将黑洞数输出 { printf("%d%c",record2[j],','); record1[k++]=record2[j]; //记录,为下次输出查重 } printf("%d",record2[j]); record1[k++]=record2[j]; //记录,为下次输出查重 printf("%c",']'); printf("\n"); return; //输出完毕,退出 } } record2[f++]=n; Find(n); //递归调用 }
这段代码用了递归调用,可以一直求最大值与最小值的差直到出现循环。这种思路就比前面的那种要好很多。输出结果如下:
当然这段代码还有些问题,虽然可以输出正确结果,但是效率不高,从10000检测到99999要把所有的5位数都检测一遍,很复杂。不过现在我还没想到更高效率的解法,以后努力吧。