重现开始战斗12-编程之美-找符合条件的整数

问题描述:

任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0.

 

分析与解法一:

最简单的方法就是从2开始,依次遍历M,直到找N*M中只含1和0位置,这种方法很不好,例如N=99时,M=1122334455667789,N*M=111111111111111111.这个所花费的时间显然是我们不希望看到的。

 

分析与解法二:

对于遍历M,我们不如直接遍历M*N,因为M*N比较有特征性,只含0与1,因此,要遍历一个K为,只含0和1的数,我们仅仅需要的遍历的次数为2K,遍历从1开始,遍历的学列为1,10,11,100,101….注意,可以发现,这其实一颗二叉树,10和11可以看做是1的左右儿子,100和101也看成是10的左右儿子,…,照这个规律,其生成的二叉树如下:


每个节点下面的数字为除3的余数。

这里可以发现一个规律,就是如果一个节点的余数与另一个节点的余数相同,那么他们的儿子节点余数也一样,发现这一点就可以对上面的二叉树剪枝。之所以是这样,是因为同余定理。

同余定理:

如果a%c = d; b%c = d,那么(a+x)%c == (b+x)%c == d。

因为a%c == b%c == d;

所以a = a1*c+d,b = b1*c+d;

进而a+x = a1*c+d+x,b+x = b1*c+d+x;

所以(a+x)%c == (b+x)%c == x+d;

 

算法思路:

总体来说采用的深度优先的策略,遍历上图中的某一层的某一个节点,只要除N的余数不为0,则入栈(入栈同时根据同余定理进行剪枝,如果该节点与其他遍历过的节点同余,该节点将不会入栈),如果余数为0则找到了M,程序退出。如果没有发现有效M,则依次弹出栈,进行X*10和X*10+1扩展,然后重复上述步骤。其中,剪枝通过采用一个N维行向量进行记录是否出现同余的情况实现。

1.      初始化余数数组model[N],全为0;

2.      M*N = 1,将1进栈;计算1%N,如果1%N==0,算法结束,否则将model[1]设置为1;

3.      M*N = M*N*10,计算M*N%N,如果为0,算法结束,否则,如果model[M*N%N]==1,跳到4,否则M*N入栈;

4.      M*N = M*N*10+1,计算M*N%N,如果为0,算法结束,否则,如果model[M*N%N]==1,跳到5,否则M*N入栈;

5.      弹出栈元素,跳到3;


在进行这个题目的时候,在调试的时候发现有个问题,就是大整数的溢出问题,我的程序只能处理64位的ULL。下面是代码:

 

#include <iostream>

#include <climits>

 

#include "Stack.h"

#include "Queue.h"

 

using namespace std;

 

int main()

{

    cout << "Pleaseinput N:";

    unsigned long long N;

    cin >> N;

 

    bool *model= new bool[N];

    for(int i = 0; i < N; i++)

        model[i] = 0;

 

    unsigned long long count = 0;

    Queue<unsignedlong long>s;

    unsigned long long X = 1;

    long long m = X%N;

    s.EnQueue(X);

    model[m] = true;

 

    while(!s.QueueIsEmpty())

    {

        count++;

 

        unsignedlong long tmp;

        s.DeQueue(tmp);

       

        long long tmpmode;

 

        if(tmp< ULLONG_MAX/10)

        {

            unsignedlong long tmpX;

            tmpX = tmp*10;

            tmpmode = tmpX%N;

            if(tmpmode== 0)

            {

                X = tmpX;

                break;

            }

            else

            {

                if(!model[tmpmode])

                {

                    model[tmpmode] = true;

                    s.EnQueue(tmpX);

                }

            }

        }

 

        if(tmp< LLONG_MAX/10)

        {

            unsignedlong long tmpX;

            tmpX = tmp*10+1;

            tmpmode = tmpX%N;

            if(tmpmode== 0)

            {

                X= tmpX;

                break;

            }

            else

            {

                if(!model[tmpmode])

                {

                    model[tmpmode] = true;

                    s.EnQueue(tmpX);

                }

            }

        }

    }

 

    cout << "TheM is " << X/N << endl;

    cout << "count= " << count << endl;

 

    system("pause");

    return 0;

}

运行结果:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值