题目描述:
在计算机科学上,有很多类问题是无法解决的,我们称之为不可解决问题。然而,在很多情况下我们并不知道哪一类问题可以解决,哪一类问题不可解决。现在我们就有这样一个问题,问题如下:
(1) 输入一个正整数n;
(2) 把n显示出来;
(3) 如果n=1则结束;
(4) 如果n是奇数则n变为3n+1,否则n变为n/2;
(5) 转入第(2)步。
例如对于输入的正整数22,应该有如下数列被显示出来:
22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
我们推测:对于任意一个正整数,经过以上算法最终会推到1.尽管这个算法很简单,但是我们仍然无法确定我们的推断是否正确。不过好在我们有计算机,我们验证了对于小于1000000的正整数都满足以上推断。
对于给定的正整数n,我们把显示出来的数的个数定义为n的链长,例如22的链长为16.
你的任务是编写一个程序,对于任意一对正整数i和j,给出i与j之间的最长链长,当然这个最长链长是由i与j之间的其中一个正整数产生的。我们这里的i与j之间即包括i也包括j。
输入格式:
两个正整数i, j 从文本文件LINK.DAT输入,文件只有一行,即为正整数i和j,i和j之间以一个空格隔开。0<i≤j<10000;
输出格式:
要求将你的程序的运行结果写入文本文件LINK.OUT中,文件只能有一行,即为i与j之间的最长链长。
输入输出样例:
输入样例:
1 10
输出样例:
20
题解:
题目开始的都是些废话~~当然ACM的题目里有很多这种啰嗦的东西,不过看起来倒是觉得挺让人心暖和的,不像国内的题目都是直接往主题裸奔。
接着给出了一个很简单的递推公式,让你将一个输入的正整数n进行反复的变换:当是偶数时 n=n/2, 奇数时n=3n+1。虽然没有说这个是得到证明的结果,但是从后面的输入格式的限制条件(0<i≤j<10000)来看,显然我们可以认为,这种变换最终会让n成为1,而这也是递推结束的标志。
然后,我们还知道这种变换所产生的数的个数被称为3n+1链的长度。而最终所要求的输出是在给出的i到j之间所有正整数的3n+1链长之中最长的一个。例如对样例来说:
正整数 1 2 3 4 5 6 7 8 9 10
对应的3n+1链长 1 2 8 3 6 9 17 4 20 7
最长的链长应该为n=9时的链长:20
分析:
这其实是很简单的一题,如果需求仅仅是解决规模0到10000的数,那么我们只需要像用傻瓜照相机一样跟着说明做就好了。
首先,我们必须根据3n+1链的说明根据一个数模拟实现整个链,然后统计其链长。
之后,我们可以根据这个实现,计算从i到j所有整数的链长,并保留其中最大的那个即可。
参考代码:
#include <iostream>
#include <fstream>
using namespace std;
// 求n的3n+1链长函数
int LnkLength( int n)
{
int Length = 1;
while (n!=1)
{
if ( n&1 == 1 ) // 奇偶性判断
n = 3*n + 1;
else
n=n>>1; // 移位操作一般比除法操作快(不考虑编译器优化)
++Length;
}
return Length;
}
int main()
{
ifstream infile("LINK.DAT", ios::in );
ofstream outfile("LINK.OUT", ios::out );
int max=0;
int i,j,Length;
infile>>i>>j;
for ( int idx=i; idx<=j; ++idx)
{
Length = LnkLength(idx);
max = Length>max? Length:max;
}
outfile<<max;
return 0;
}
测试输入1: 测试输入2: 测试输入3:
8 8 100 200 201 210
测试输出1: 测试输出2: 测试输出3:
4 125 89