UVA 100 - The 3n+1 problem (3n+1 问题)

100 - The 3n+1 problem (3n+1 问题)


 

/*
* 100 - The 3n+1 problem (3n+1 问题)
* 作者 仪冰
* QQ 974817955
*
* [问题描述]
* 考虑如下的序列生成算法:从整数 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。
* 这个猜想对于至少 1 000 000 内的整数都是正确的。
* 对于给定的 n,该序列的元素(包括 1)个数被称为 n 的循环节长度。
* 在上述例子中,22 的循环节长度为 16。
* 输入两个数 i 和 j,你的任务是计算 i 到 j(包含 i 和 j)之间的整数中,
* 循环节长度的最大值。

* [输入]
* 输入每行包含两个整数 i 和 j。所有整数大于 0,小于 1 000 000。
*
* [输出]
* 对于每对整数 i 和 j,按原来的顺序输出 i 和 j,然后输出二者之间的整数中的最大循环节长度。
* 这三个整数应该用单个空格隔开,且在同一行输出。
* 对于读入的每一组数据,在输出中应位于单独的一行。
*
[样例输入]
1 10
100 200
201 210
900 1000

[样例输出]
1 10 20
100 200 125
201 200 27
900 1000 174

* [解题方法]
* 计算每个数的循环节长度,求给定区间的最大值。
*
* 需要注意:
* 1.中间计算过程有的数据会超出int 或 long 型范围,应该用long long型。
* 2.输入的两个数,应该比较大小,判断出左区间和右区间。
* 3.直接按部就班的计算会time limited(超时),这里采用填表的方法,
*   把算出来的结果保存在一个全局数组中,这样以后用到的时候,直接拿来用,节省时间,避免超时。
*/

#include<iostream>
#include<cstring>

using namespace std;

const int MAXN = 1000000;  //右端点最大值

int MediaVariableArray[MAXN];  //保存区间中所有的循环节长度,这是对填表的应用

int LoopNodeLength (long long EveryNumber);  //计算循环节长度

int main()
{
    int firstnumber = 0;        //输入的第一个数
    int secondnumber = 0;       //输入的第二个数
    int nodeleft = 0;           //区间左端点
    int noderight = 0;          //区间右端点
    int loopnodemax = 0;        //保存最大长度的循环节
    int everynodelength = 0;    //保存区间中单个数的循环节长度

    memset(MediaVariableArray, 0, sizeof(MediaVariableArray)); //初始化,都置为0

    while (cin >> firstnumber >> secondnumber)
    {
        if (firstnumber > secondnumber)  //判断左端点和右端点
        {
            nodeleft = secondnumber;
            noderight = firstnumber;
        }
        else
        {
            nodeleft = firstnumber;
            noderight = secondnumber;
        }

        loopnodemax = 0; //初始化最大值
        for (int i=nodeleft; i<=noderight; i++)
        {
            everynodelength = LoopNodeLength(i);

            if (everynodelength > loopnodemax) //判断是否更新最大值
            {
                loopnodemax = everynodelength;
            }
        }

        cout << firstnumber << " " <<secondnumber << " ";
        cout << loopnodemax << endl;
    }

    return 0;
}

int LoopNodeLength(long long EveryNumber)
{
    if (EveryNumber == 1)
    {
        return 1;
    }

    if (EveryNumber & 1)  //按位与1 等价于 对2求模;左移 等价于 乘以2,同理右移是除以2。
    {
        //计算n = 3n + 1;
        //左移运算符比加号优先级低,所以加括号;
        //我建议不管怎样,为了代码易读都应该加上必要的括号。
        EveryNumber = EveryNumber + (EveryNumber<<1) + 1;
    }
    else
    {
        EveryNumber >>= 1;  //n = n / 2
    }

    //如果中间计算值小于MAXN就看看我们先前求没求出它的循环节长度,
    //如果求出来了,直接拿来用,节省时间。
    if (EveryNumber < MAXN)
    {
        if (MediaVariableArray[EveryNumber] == 0)
        {
            MediaVariableArray[EveryNumber] = LoopNodeLength(EveryNumber);
        }

        return MediaVariableArray[EveryNumber] + 1;
    }

    return LoopNodeLength(EveryNumber) + 1;
}


 

 

posted on 2013-09-25 19:38  you Richer  阅读(205)  评论(0编辑  收藏  举报