poj1426 bfs+同余数定理

题意大致是给定一个n(1~200),求出n的一个倍数k,但k的每一位都必须是0或1。Special Judge 的意思是答案不唯一,比如说n=2时,k=10对,k=100也对,只要输出一个符合要求的即可,没有说要最小。这个题有点像数学题,但还用到了bfs,还算比较综合吧。难点在于k比较大的时候如何表示,很显然一个int,long都是不行的。还有就是数很大时穷举去计算是否能整除也很浪费时间。

解决的办法是利用同余数定理。

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

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

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

以n=6为例,

比如说k=110时,余数是2,现在要求1100和1101的余数是否为0,下面求1101的余数:

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

110%6正好是少一位的数(k=110)的余数,是我们已经求过且保存在mod【】中的,所以说求1100和1101时只需知道110的余数,用(余数*10+0/1)%6就可以表示1100/1101%6了。

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

接下来的问题就是当计算出某个k值得余数为0了,但是怎么知道k是多少?因为我们只保存了k的每个余数,并没有保存k的每一位,事实上也没有办法保存k的每一位,因为到后面可能性越来越多。这个时候我发现可以利用mod【i】数组中的下表i来计算,因为下标i正好是k的十进制的表示!因为k只有0,1组成,按照小到大的顺序不重复不遗漏地取值正好相当于是二进制计数(好好体会一下~)

我的代码:

#include<iostream>
using namespace std;

int mod[600000];			//开大一点防止RE
int main(){
	int n;
	while(cin>>n&& n){
		memset(mod,-1,sizeof(mod));
		int output[110];
		int front,rear;
		front=rear=1;
		mod[rear++]=1%n;
		while(front<rear){
			int tmp=mod[front++];
			if ((tmp*10)%n==0) break;				//利用前一步的余数代替进行计算
			else mod[rear++]=(tmp*10)%n;
			if ((tmp*10+1)%n==0) break;
			else mod[rear++]=(tmp*10+1)%n;
		}
		int i=0;
		while(rear>1){								//根据rear值计算各位,其实就是将rear值用二进制表示
			output[i++]=rear%2;
			rear=rear/2;
		}
		output[i]=rear;
		for (int j=i;j>=0;j--){						//逆向输出即可
			cout<<output[j];
		}
		cout<<endl;
	}
	return 0;
}

欢迎交流讨论~

你可能感兴趣的:(C++,数学,ACM,poj,bfs)