poj 1426

题目链接:http://poj.org/problem?id=1426

转载请注明出处:優YoU  http://user.qzone.qq.com/289065406/blog/1303946967

大致题意:

给出一个整数n(1 <= n <= 200)。求出任意一个它的倍数m,要求m必须只由十进制的'0''1'组成。

 

解题思路:

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

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

 

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

 

不说废话。

 

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

我以n=6为例

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

 

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

现在就是要用BFSk

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=10k=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=100k=101k=110k=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),因为每一位都只有01两种选择,换而言之是一个双入口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

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

 

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

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

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

答案是可以的

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

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

n200的范围内, 余数值不可能超过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值有限,只是1200的整数,因此本题也可以用打表做,通过上面的方法得到结果后,就把1~200的倍数打印出来,重新建立一个程序,直接打表就可以了。

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

 

//BFS+同余模

//Memory Time

//2240K  32MS

 

#include<iostream>

using namespace std;

 

int mod[524286];  //保存每次mod n的余数

                  //由于198的余数序列是最长的

                  //经过反复二分验证,436905是能存储198余数序列的最少空间

                  //POJ肯定又越界测试了...524286AC的最低下限,不然铁定RE

 

int main(int i)

{

      int n;

      while(cin>>n)

      {

           if(!n)

                 break;

 

           mod[1]=1%n;  //初始化,n倍数的最高位必是1

 

           for(i=2;mod[i-1]!=0;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 pm=0;

           while(i)

           {

                 mod[pm++]=i%2;   //*10操作转化为%2操作,逆向求倍数的每一位数字

                 i/=2;

           }

           while(pm)

                 cout<<mod[--pm];  //倒序输出

           cout<<endl;

      }

      return 0;

}

 

 

 //直接打表

//Memory Time

//180K  16MS

 

#include<iostream>

using namespace std;

 

char table[201][30]={"0","1","10","111","100","10","1110","1001","1000","111111111","10","11","11100","1001","10010","1110","10000","11101","1111111110","11001","100","10101","110","110101","111000","100","10010","1101111111","100100","1101101","1110","111011","100000","111111","111010","10010","11111111100","111","110010","10101","1000","11111","101010","1101101","1100","1111111110","1101010","10011","1110000","1100001","100","100011","100100","100011","11011111110","110","1001000","11001","11011010","11011111","11100","100101","1110110","1111011111","1000000","10010","1111110","1101011","1110100","10000101","10010","10011","111111111000","10001","1110","11100","1100100","1001","101010","10010011","10000","1111111101","111110","101011","1010100","111010","11011010","11010111","11000","11010101","1111111110","1001","11010100","10000011","100110","110010","11100000","11100001","11000010","111111111111111111","100","101","1000110","11100001","1001000","101010","1000110","100010011","110111111100","1001010111","110","111","10010000","1011011","110010","1101010","110110100","10101111111","110111110","100111011","111000","11011","1001010","10001100111","11101100","1000","11110111110","11010011","10000000","100100001","10010","101001","11111100","11101111","11010110","11011111110","11101000","10001","100001010","110110101","100100","10011","100110","1001","1111111110000","11011010","100010","1100001","11100","110111","11100","1110001","11001000","10111110111","10010","1110110","1010100","10101101011","100100110","100011","100000","11101111","11111111010","1010111","1111100","1111110","1010110","11111011","10101000","10111101","111010","1111011111","110110100","1011001101","110101110","100100","110000","100101111","110101010","11010111","11111111100","1001111","10010","100101","110101000","1110","100000110","1001011","1001100","1010111010111","110010","11101111","111000000","11001","111000010","101010","110000100","1101000101","1111111111111111110","111000011","1000"};

 

int main(void)

{

      int n;

      while(cin>>n)

      {

           if(!n)

                 break;

           cout<<table[n]<<endl;

      }

      return 0;

}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值