POJ 1207 3n+1问题

描述

考虑下列算法:

 1. input n
 2. print n
 3. if n = 1 then STOP
 4.     if n is odd then n <- 3n+1
 5.     else n <- n/2
 6. GOTO 2

输入:22,
打印序列:22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1

假设对于所有整数值,算法可终止(打印到1时),但不知道假设是否为真。对于所有的整数n,例如 0<n<1,000,000 是已验证的。

给定输入n,可能确定在打印到1时一共输出的数字的个数,该个数成为n的周期长度,对于上面的例子,22的周期长度为16.

对于任意两个数i,j,找到i,j之间的所有整数中,最大的周期长度。

输入

输入是一系列整数对i,j,每行一对整数。所有整数n, 0<n<10,000 ,需要测试[i,j]内的所有整数,来确定最大的周期长度

输出

对于每对输入整数i,j,需要输出i,j和[i,j]区间内的最大的周期长度。这三个数用至少一个空格分开,位于一行内。输出的i,j要和输入的顺序相同。

输入样例

1 10
100 200
201 210
900 1000

输出样例

1 10 20
100 200 125
201 210 89
900 1000 174

思路

直接计算的话,由于数据计算量在10,000内,可以
打表的话,与直接计算只能节省重复区间的计算量,不会快很多
最快的算法是深度搜索+记忆优化(记忆优化相当于打表),适用于n为更大的数时,作为拓展了解一下吧。。。
注意:
1. 打印1本身也算在周期内
2. 每对数i,j不一定是i < j,需要格外留意,输出时要按照输入时的顺序输出

1 直接计算

164K 16MS C 859B

#include <stdio.h>

int steps(n)
{
    int count = 1;
    while(n != 1)
    {
        if(n&1)//按位与运算,奇数的二进制末尾为1,奇数&1结果为1
        {
            //设 k = n/2, 则 3(2k+1)+1 = (3k+2)*2,减少一次迭代
            n = n/2 * 3 + 2;
            count += 2;
        }
        else
        {
            n /= 2;
            ++count;
        }
    }
    return count;
}

int main()
{
    int i,j,max,t,k,k_step;//t用于交换,k用于遍历
    int tmp_i,tmp_j;//用于保存输入的i,j的顺序
    while(scanf("%d%d",&i,&j)==2)
    {
        tmp_i = i;
        tmp_j = j;
        if(i>j){
            t = i;
            i = j;
            j = t;
        }
        max = steps(i);
        for(k = i+1; k <= j; ++k)
        {
            k_step = steps(k);
            if(max < k_step)
                max = k_step;
        }
        printf("%d %d %d\n", tmp_i,tmp_j,max);
    }
    return 0;
}

2 打表

204K 0MS C 924B

#include <stdio.h>

int steps_arr[10001] = {0};//1开始计数
void calcSteps()
{
    int i,n;
    for(i=1; i < 10001; ++i)
    {
        steps_arr[i] = 1;
        n = i;
        while(n!=1)
        {
            if(n&1){
                n = n/2 * 3 + 2;
                steps_arr[i] = steps_arr[i] + 2;
            }
            else
            {
                n /= 2;
                steps_arr[i] = steps_arr[i] + 1;
            }

        }
    }
}

int main()
{
    int i,j,t,k,max;
    int temp_i,temp_j;
    calcSteps();
    while(scanf("%d%d",&i,&j)==2)
    {
        temp_i = i;
        temp_j = j;
        if(i > j)
        {
            t = i;
            i = j;
            j = t;
        }
        max = steps_arr[i];
        for(k = i+1; k <= j; ++k)
        {
            if(max < steps_arr[k])
                max = steps_arr[k];
        }
        printf("%d %d %d\n",temp_i, temp_j, max);
    }
    return 0;
}

深度搜索+记忆优化

244K 0MS C 941B

#include <stdio.h>
int steps_arr[20001] = {0};
int dfs(int n)
{
    if(n==1)
        return 1;
    if(n > 20000)
    {
        if(n&1)
            return dfs(n/2 *3 + 2) + 2; //这里就是深度搜索,向下递归一层
        else
            return dfs(n/2) + 1;
    }
    if(!steps_arr[n]) //没有求过n的步数,则需要求(否则可以用上次返回的值【记忆】)
    {
        if(n&1)
            return dfs(n/2 *3 + 2) + 2;
        else
            return dfs(n/2) + 1;
    }
    else
        return steps_arr[n];
}

int main()
{
    int i,j,max,k,t;
    steps_arr[1] = 1;
    for(i = 1; i < 10001; ++i)
    {
        steps_arr[i] = dfs(i);//打表,主要保存那些频繁计算的数的步数
    }
    while(scanf("%d%d",&i,&j)==2)
    {
        printf("%d %d ",i,j);
        max = 0;
        if(i > j)
        {
            t = i;
            i = j;
            j = t;
        }
        for(k = i; k <= j; ++k)
        {
            if(max < steps_arr[k])
                max = steps_arr[k];
        }
        printf("%d\n", max);
    }
    return 0;
}

参考

  1. http://www.cnblogs.com/yinger/archive/2011/07/15/2107761.html
  2. http://www.cnblogs.com/CocoonFan/archive/2013/03/02/2940336.html
  3. https://github.com/YvesChan/codebak/blob/master/POJ%201207%20模拟水过,内有对比程序.cpp

你可能感兴趣的:(POJ 1207 3n+1问题)