C语言程序设计100例之(57):3n+1问题
例57 3n+1问题
问题描述
考虑如下的序列生成算法:从整数 n 开始,如果 n 是偶数,把它除以 2;如果 n 是奇数,把它乘 3 加1。用新得到的值重复上述步骤,直到 n = 1 时停止。例如,n = 22 时该算法生成的序列是:22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1。
人们猜想(没有得到证明)对于任意整数 n,该算法总能终止于 n = 1。这个猜想对于至少 1000 000内的整数都是正确的。
对于给定的 n,该序列的元素(包括 1)个数被称为 n 的循环节长度。在上述例子中,22 的循环节长度为 16。
输入两个数 i 和 j,你的任务是计算 i 到 j(包含 i 和 j)之间的整数中,循环节长度的最大值。
输入格式
输入每行包含两个整数 i 和 j。所有整数大于 0,小于 1 000000。
输出格式
对于每对整数 i 和 j,按原来的顺序输出 i 和 j,然后输出二者之间的整数中的最大循环节长度。这三个整数应该用单个空格隔开,且在同一行输出。对于读入的每一组数据,在输出中应位于单独的一行。
样例输入
1 10
100 200
201 210
900 1000
样例输出
1 10 20
100 200 125
201 210 89
900 1000 174
(1)编程思路。
计算给定区间中每个数的循环节长度,用max保存这些数的循环节长度的最大值。
定义数组int cache[1000000]={0},其中cache[i]保存整数i的循环节长度,在计算过程中采用填表的方法保存已有计算结果,可以显著减少计算时间。例如,若已计算出5的循环节长度为6(cache[5]=6),则10的循环节长度为cache[10]=cache[5]+1。
另外,在中间计算过程可能会超过 int(4字节存储空间) 型数据所能表示数的范围,故采用long long (8 字节存储空间)型整数来运算。
(2)源程序。
#include <stdio.h>
#define MAXSIZE 1000000
int cache[MAXSIZE]={0};
int counter(long long number)
{
if (number == 1)
return 1;
if (number%2==1)
number=3*number+1;
else
number/=2;
if (number < MAXSIZE )
{
if (!cache[number])
cache[number] = counter(number);
return 1 + cache[number];
}
return 1 + counter(number);
}
int main()
{
int begin,end;
while (scanf("%d%d",&begin,&end)!=EOF)
{
int max=0,t;
int left=begin<end?begin:end;
int right=begin>end?begin:end;
for (int k = left; k <= right; k++)
{
t=counter(k);
if (t>max) max=t;
}
printf("%d %d %d\n",begin,end,max);
}
return 0;
}
习题57
57-1 角谷步数
问题描述
你听说过角谷猜想吗?
任意的正整数,比如 5, 我们从它开始,如下规则计算:
如果是偶数,则除以2,如果是奇数,则乘以3再加1。
如此循环,最终必会得到“1” !
比如 5 的处理过程是:5 ---> 16 ---> 8 ---> 4 ---> 2 ---> 1。
一个正整数经过多少步才能变成1, 称为角谷步数。
对于5而言,步数也是5;对于1,步数为0。
编写程序,对于输入的整数n,求角谷步数为n的最小的正整数。
输入
输入包括多行,每行包含一个整数N(1<=N<=300)。
输出
对于输入的每个整数N,输出一行,包含一个整数,表示第N个幸运数字。
输入样例
3
4
7
输出样例
8
16
3
(1)编程思路。
同例57一样,定义数组int cache[1000000]={0},其中cache[i]保存整数i的循环节长度。cache[i]-1的值就是整数i的角谷步数。
要求角谷步数为n的最小的正整数,从1开始搜索,若某个数i的cache[i]==n+1,则i就是所求的最小整数。
(2)源程序。
#include <stdio.h>
#define MAXSIZE 1000000
int cache[MAXSIZE]={0};
int counter(long long number)
{
if (number == 1)
return 1;
if (number%2==1)
number=3*number+1;
else
number/=2;
if (number < MAXSIZE )
{
if (!cache[number])
cache[number] = counter(number);
return 1 + cache[number];
}
return 1 + counter(number);
}
int main()
{
int n;
while (scanf("%d",&n)!=EOF)
{
int i,t;
for (i=1; ; i++)
{
t=counter(i);
if (t==n+1) break;
}
printf("%d\n",i);
}
return 0;
}
57-2 Good Numbers
问题描述
一个数N,如果它每一个位数字之和可以整除10,那么它就是Good Numbers。比如451就是一个Good Numbers,4+5+1=10,求[A,B]之间这样的数的个数。
输入
输入第1行包含一个整数T (T <= 10000) ,表示测试用例的组数;
之后有T行,每行两个整数A和B(0 <= A <= B <= 1018)。
输出
对于每个测试用例,输出信息为“Case #X:P”,其中X表示第X组测试用例(X从1开始),P表示[A,B]之间Good Numbers的个数。
输入样例
2
1 10
1 20
输出样例
Case #1: 0
Case #2: 1
(1)编程思路。
先列出前20个Good Numbers为:0,19,28,37,46,55,64,73,82,91,109,118,127,136,145,154,163,172,181,190……。由此可以发现:
[0,10]之间Good Numbers的个数为1
[0,100]之间Good Numbers的个数为10
[0,1000]之间Good Numbers的个数为100
[0,990]之间Good Numbers的个数为99
[0,992]之间Good Numbers的个数为100
……
[0,n]之间Good Numbers的个数为n/10 + (1或0),其中加1的情况为:n/10*10 到n有1个Good Numbers。例如:n=997,则Good Numbers的个数99 +1,因为990到997之间的992就是一个Good Numbers。
(2)源程序。
#include <stdio.h>
int isOne(long long n) // 例如:997计算990-997之间有没有符合条件的数
{
int s=0;
for(long long i=n/10*10;i<=n;i++)
{
long long t=i;
s=0;
while (t)
{
s+=t%10;
t/=10;
}
if (s%10==0) return 1;
}
return 0;
}
long long getNum(long long n)
{
if (n<0) return 0;
if (n<=10) return 1;
return n/10+isOne(n);
}
int main()
{
int t,cas=1;
scanf("%d",&t);
while(t--)
{
long long a,b;
scanf("%lld%lld",&a,&b);
printf("Case #%d: %lld\n",cas++,getNum(b)-getNum(a-1));
}
return 0;
}
57-3 Rabbit Number
问题描述
设 S(N ) 表示 N 的各位数字之和,如 S(484) = 4+8+4 = 16, S(22) = 2+2 = 4。如果一个正整数满足 S(x*x) = S(x) *S(x),我们称之为 Rabbit N umber。比方说,22 就是一个 Rabbit Number,因为 S(484) = S(22) *S(22)。
现在,给出一个区间 [L, R],求在该区间内的 Rabbit N umber 的个数。
输入格式
输入仅一行,为空格隔开的两个数 L 和 R(1≤L≤R≤109)。
输出格式
输出仅一行一个整数,表示所求 Rabbit N umber 的个数。
输入样例
1 58
输出样例
12
(1)编程思路。
首先,一个Rabbit Number的各位数字一定不超过3。
这个结论可简单推出。设某数字x为1位,x>=4,因为x*x有进位,所以s(x*x)= x*x/10+x*x%10;而s(x)*s(x)=x*x,这样,s(x*x)< <s(x)*s(x),一定不是Rabbit Number。
既然各位数字不超过3,而区间中数字的位数不超过10位,这样可以枚举每一位上的数字(0~3),对于枚举的每个数,若满足在区间[L,R]中且为Rabbit N umber的要求,则进行计数。
(2)源程序。
#include <stdio.h>
int digitSum(long long x)
{
long long a=x;
int sum=0;
while (a)
{
sum+=a%10;
a/=10;
}
return sum;
}
int main()
{
long long l,r;
int ans=0;
int a[11],i;
scanf("%lld%lld",&l,&r);
int f=1;
for (a[1]=0;a[1]<=1 && f;a[1]++)
for (a[2]=0;a[2]<=3 && f;a[2]++)
for (a[3]=0;a[3]<=3 && f;a[3]++)
for (a[4]=0;a[4]<=3 && f;a[4]++)
for (a[5]=0;a[5]<=3 && f;a[5]++)
for (a[6]=0;a[6]<=3 && f;a[6]++)
for (a[7]=0;a[7]<=3 && f;a[7]++)
for (a[8]=0;a[8]<=3 && f;a[8]++)
for (a[9]=0;a[9]<=3 && f;a[9]++)
for (a[10]=0;a[10]<=3 && f;a[10]++)
{
long long num=0;
for (i=1;i<=10;i++)
{
num=num*10+a[i];
}
if (num>r)
{
f=0;
break;
}
if(num>=l&&num<=r)
{
if(digitSum(num)*digitSum(num)==digitSum(num*num)) ans++;
}
}
printf("%d\n",ans);
return 0;
}