zoukankan      html  css  js  c++  java
  • Poj1426

    Find The Multiple
    Time Limit: 1000MS   Memory Limit: 10000K
    Total Submissions: 25721   Accepted: 10608   Special Judge

    Description

    Given a positive integer n, write a program to find out a nonzero multiple m of n whose decimal representation contains only the digits 0 and 1. You may assume that n is not greater than 200 and there is a corresponding m containing no more than 100 decimal digits.

    Input

    The input file may contain multiple test cases. Each line contains a value of n (1 <= n <= 200). A line containing a zero terminates the input.

    Output

    For each value of n in the input print a line containing the corresponding value of m. The decimal representation of m must not contain more than 100 digits. If there are multiple solutions for a given value of n, any one of them is acceptable.

    Sample Input

    2
    6
    19
    0

    Sample Output

    10
    100100100100100100
    111111111111111111

    Source

    题意:

    给一个数n,让你找出一个只有1,0,组成的十进制数,要求是找到的数可以被n整除。

    做法:

    假如n=6;

    1余n=1;

    10余n=1*10%6=4;

    11余n=(1*10+1)%6=5;

    100余n=4*10%6=4;

    101余n=(4*10+1)%6=5;

    110余n=5*10%6=2;

    111余n=(5*10+1)%6=3;

    可以推知10。。。。01余n=(10。。。。0余n*10+1)余n
     

    解题思路:

    首先暴力枚举肯定是不可能的 1000ms 想不超时都难,而且枚举还要解决大数问题。。

    要不是人家把这题放到搜索,怎么也想不到用BFS。。。

    解题方法: BFS+同余模定理

    不说废话。

     

    首先说说朴素的不剪枝搜索方法:

    我以n=6为例

    首先十进制数,开头第一个数字(最高位)一定不能为0,即最高位必为1

    设6的 ”01十进制倍数” 为k,那么必有k%6 = 0

    现在就是要用BFS求k值

    1、先搜索k的最高位,最高位必为1,则此时k=1,但1%6 =1  !=  0

    因此k=1不是所求,存储余数 1

    2、搜索下一位,下一位可能为0,即 k*10+0,此时k=10,那么k%6=4

    可能为1,即 k*10+1,此时k=11,那么k%6=5

    由于余数均不为0,即k=10与k=11均不是所求

    3、继续搜索第三位,此时有四种可能了:

    对于k=10,下一位可能为0,即 k*10+0,此时k=100,那么k%6=4

                  下一位可能为1,即 k*10+1,此时k=101,那么k%6=5

    对于k=11,下一位可能为0,即 k*10+0,此时k=110,那么k%6=2

                  下一位可能为1,即 k*10+1,此时k=111,那么k%6=3

    由于余数均不为0,即k=100,k=101,k=110,k=111均不是所求

    4、继续搜索第四位,此时有八种可能了:

    对于k=100,下一位可能为0,即 k*10+0,此时k=1000,那么k%6=4

                  下一位可能为1,即 k*10+1,此时k=1001,那么k%6=5

    对于k=101,下一位可能为0,即 k*10+0,此时k=1010,那么k%6=2

                  下一位可能为1,即 k*10+1,此时k=1011,那么k%6=3

    对于k=110,下一位可能为0,即 k*10+0,此时k=1100,那么k%6=2

                  下一位可能为1,即 k*10+1,此时k=1101,那么k%6=3

    对于k=111,下一位可能为0,即 k*10+0,此时k=1110,那么k%6=0

                  下一位可能为1,即 k*10+1,此时k=1111,那么k%6=1

    我们发现k=1110时,k%6=0,即1110就是所求的倍数

     

    从上面的演绎不难发现,用BFS是搜索 当前位数字 (除最高位固定为1),因为每一位都只有0或1两种选择,换而言之是一个双入口BFS

    本题难点在于搜索之后的处理:对余数的处理,对大数的处理,余数与所求倍数间的关系

     

    接下来说说处理大数问题和剪枝的方法:

    首先我们简单回顾一下 朴素搜索 法:

    n=6

     

    1%6=1  (k=1)

     (1*10+0)%6=4  (k=10)

    {

        (10*10+0)%6=4   (k=100)

        {

            (100*10+0)%6=4  (k=1000)

            (100*10+1)%6=5  (k=1001)

        }

        (10*10+1)%6=5  (k=101)

        {

            (101*10+0)%6=2  (k=1010)

            (101*10+1)%6=3  (k=1011)

        }

    }

       (1*10+1)%6=5  (k=11)

    {

        (11*10+0)%6=2   (k=110)

        {

            (110*10+0)%6=2  (k=1100)

            (110*10+1)%6=3  (k=1101)

        }

        (11*10+1)%6=3   (k=111)

        {

            (111*10+0)%6=0  (k=1110)   有解

            (111*10+1)%6=1  (k=1111)  由于前面有解,这个余数不存储

        }

    }

    }

    从上面可以看出余数的存数顺序(逐层存储):

    用数组mod[]存储余数,其中mod[0]不使用,由mod[1]开始

    那么mod中的余数依次为: 1 4 5 4 5 2 3 4 5 2 3 2 3 0  共14个

    即说明我们得到 余数0 之前,做了14步*10的操作,那么当n值足够大的时候,是很容易出现k为大数的情况(事实上我做过统计,200以内的n,有18个n对应的k值为大数

    那么我们再用int去存储k就显得不怎么明智了。

    为了处理所有情况,我们自然会想到 是不是应该要用int[]去存储k的每一位?

    而又由于k是一个01序列,那能不能把 *10得到k每一位的问题 转化为模2的操作得到k的每一位(0或1) 呢?

    答案是可以的

    首先我们利用 同余模定理 对得到余数的方式进行一个优化

    (a*b)%n = (a%n *b%n)%n

    (a+b)%n = (a%n +b%n)%n

    随便抽取上面一条式子为例

    前一步 (11*10+1)%6=2   即k=110 , k%6=2

    当前步 (110*10+1)%6=2

    由同余模定理  (110*10+1)%6 = ((110*10)%6+1%6 )%6 = ((110%6 * 10%6)%6 +1 )%6

    不难发现下划线部分110%6等于 (11*10+0)%6 = 2

    所以当前步(110*10+1)%6可以转变为  (2*10+1)%6=2

     

    很显然地,这种处理把k=110 等价于 k=2

    即用 前一步操作得到的余数 代替 当前步的k值

    而n在200的范围内, 余数值不可能超过3位数, 这就解决了 大数的问题

     

    通过这种处理手法,我们只需在BFS时顺手存储一个 余数数组mod[] ,就能通过mod[i-1]得到mod[i]  ,直到mod[i]==0 时结束,大大减少了运算时间

    前面已经提到,n=6时,求余操作进行了14次,对应地,BFS时*10的操作也进行了14次。

    令i=14,通过观察发现,i%2恰好就是 6 的倍数的最低位数字

    i/2  再令 i%2 ,恰好就是 6 的倍数的 次低位数字。。。

    循环这个操作,直到i=0,就能得到 6的 01倍数(一个01队列),倒序输出就是所求

    这样就完成了 *10操作到 %2操作的过渡

      

    由于n值有限,只是1到200的整数,因此本题也可以用打表做,通过上面的方法得到结果后,就把1~200的倍数打印出来,重新建立一个程序,直接打表就可以了。

    不过打表比上面介绍的方法快不了多少

    #include<cstdio>
    #include<iostream>
    using namespace std;
    #define N 550000
    int n,mod[N];
    int main(){
        while(scanf("%d",&n)&&n){
            int i;mod[i=1]=1%n;//初始化,n倍数的最高位必是1  
            for(i=2;mod[i-1];i++)//利用同余模定理,从前一步的余数mod[i/2]得到下一步的余数mod[i]  
                mod[i]=(mod[i/2]*10+i%2)%n;//mod[i/2]*10+i%2模拟了BFS的双入口搜索   //当i为偶数时,+0,即取当前位数字为0  。为奇数时,则+1,即取当前位数字为1              
            i--;
            int p=0;
            while(i)
                mod[p++]=i%2,i>>=1;//把*10操作转化为%2操作,逆向求倍数的每一位数字     
            while(p)
                cout<<mod[--p];//倒序输出  
            cout<<endl;
        }
        return 0;
    }

    xzc's code:

    #include<queue>
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    int n;
    
    void bfs(int k)
        {
            queue<long long>que;
            que.push(1);
            while (!que.empty())
               {
                    long long now=que.front();
                    if (now%n!=0)
                       {
                          que.push(now*10);
                       que.push(now*10+1);          
                    }
                    else {cout<<now<<endl;return;}
                que.pop();    
               }
        }
    
    int main()
       {
            while (scanf("%d",&n)==1&&n)
               {
                  bfs(n);    
            }
           return 0;
       }
  • 相关阅读:
    "《算法导论》之‘图’":不带权二分图最大匹配(匈牙利算法)
    "C#":MySql批量数量导入
    MySql常用操作语句(2:数据库、表管理以及数据操作)
    MySql常用操作语句(1:启动、连接数据库及用户操作)
    HTML 段落
    HTML 标题
    HTML 属性
    HTML 元素
    HTML 基础
    HTML 编辑器
  • 原文地址:https://www.cnblogs.com/shenben/p/5575447.html
Copyright © 2011-2022 走看看