poj 1416 Find The Multiple 同余模定理

原来我用宽度搜索,结果TLE

#include "iostream"
#include "queue"
using namespace std;
typedef unsigned long long type;

queue<type> Q;
int chushu;
type a, b;
void bfs(){
	a = 1;
	Q.push(a);
	while(!Q.empty()){
		b = Q.front();
		Q.pop();
		int i = 2;
		while(i--){
			type m = b;
			m = m * 10 + i % 2;
			if(m % chushu == 0){			//如果可以整除, 那么输出
				cout<<m<<endl;
				return;
			}
			Q.push(m);
		}
	}
}

int main(){
	while(cin>>chushu && chushu){
		while(!Q.empty())
			Q.pop();
		bfs();
	}
	return 0;
}
网上说用queue, 就会TLE, 下面两个程序一个用 同余模定理

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

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

下面解析到第一个程序来自http://user.qzone.qq.com/289065406/blog/1303946967

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

我以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就是所求的倍数

程序:

//Memory Time  
//2236K  32MS   
  
#include<iostream>  
using namespace std;  
  
int mod[524286];  //保存每次mod n的余数  
                  //由于198的余数序列是最长的  
                  //经过反复二分验证,436905是能存储198余数序列的最少空间  
                  //但POJ肯定又越界测试了...524286是AC的最低下限,不然铁定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;  
}

下面的程序不用queue,采用同样的机理实现了BFS, 非常值得学习,来自http://hi.baidu.com/cibiluozu/item/b80a1b14a984ee5ee75e0656

#include<iostream>
using namespace std;
long long a[23213213];
void BFS(int n)
{
   int left = 0;int right = 0;
   a[right] = 1;
   right++;
   while(left<right)
   {
      if((a[left]*10%n))		//填0不能整除
	  {
		 a[right] = a[left]*10;
	     right++;
	  }
	  else						//填0后可以整除
	  {
		 cout<<a[left]*10<<endl;
		 return ;
	  }
	  if((a[left]*10+1)%n)		//填1不能整除
	  {
			a[right] = a[left]*10+1;
			right++;
	  }
	  else						//填1后可以整除
	  {
			cout<<a[left]*10+1<<endl;
			return;
	  }
	  left++;
	}
}
int main()
{
	int n;
	while(cin>>n&&n)
	{
		 if(n==1)
			 cout<<1<<endl;
		 else
			 BFS(n);
	}
 return 0;
}


从他这里还学到了另一个方法:打表, 这个方法的确很聪明

你的程序超时,但是可以得出正常结果, 想这题你就可以先用你的程序得出全部结果然后, 把结果在粘贴一个txt上保存

,然后写一新程序, 把前面的定义为数组。例子:

#include<iostream>
using namespace std;
int main()
{
 long long  a[205] = {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,
1111111101,
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,
11111111010,
110,
1001000,
11001,
11011010,
11011111,
11100,
100101,
1110110,
1111011111,
1000000,
10010,
1111110,
1101011,
1110100,
11111001,
10010,
10011,
111111111000,
10001,
1110,
11100,
1100100,
1001,
101010,
11101001,
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,
110100111,
111111110100,
1111111011,
110,
111,
10010000,
1011011,
110010,
1101010,
110110100,
11111110101,
110111110,
110001101,
111000,
11011,
1001010,
10011100011,
11101100,
1000,
11110111110,
11010011,
10000000,
100100001,
10010,
101001,
11111100,
11101111,
11010110,
11111111010,
11101000,
10001,
111110010,
110110101,
100100,
10011,
100110,
1001,
1111111110000,
11011010,
100010,
1100001,
11100,
110111,
11100,
1110001,
11001000,
11111011011,
10010,
1110110,
1010100,
10101101011,
111010010,
100011,
100000,
11101111,
11111111010,
1010111,
1111100,
1111110,
1010110,
11111011,
10101000,
10111101,
111010,
1111011111,
110110100,
1111111011,
110101110,
100100,
110000,
101110011,
110101010,
11010111,
11111111100,
1001111,
10010,
100101,
110101000,
1110,
100000110,
1001011,
1001100,
1111111100001,
110010,
11101111,
111000000,
11001,
111000010,
101010,
110000100,
1101000101,
1111111111111111110,
111000011,
1000,
10010001,
1010,
11010111,
10001100
};
int n;
while(cin>>n&&n)
{
 cout<<a[n]<<endl;
}
 return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值