其他分享
首页 > 其他分享> > 51nod 01组成的N的倍数 题解

51nod 01组成的N的倍数 题解

作者:互联网

前言
发现一道好题目。题目广搜,需要绝妙的数学证明与剪枝,思维含量较大,搜索玩出花来。
若在比赛中肯定想不出正解(话说这篇题解也不是最优解)。我的数学太弱了……抽屉原理都不知道……
看题解吧: )


题目

给定一个自然数N,找出一个M,使得M>0且M是N的倍数,并且M的10进制表示只包含0或1。求最小的M。
例如:N = 4,M = 100。
输入
输入1个数N。(1≤N≤106)
输出
输出符合条件的最小的M。
输入样例
4
输出样例
100


题解

首先对M存在进行证明。
考虑数列:1,11,111,…,11…11(N个1)。由抽屉原理知其中要么有数能被N整除,要么有两个数除N同余。若有数能整除,命题显然成立;若有两数除N同余,用大数减去小数,得到的数形如11…10…00,,能被N整除,命题也成立。
接着搜索构造M。最开始的数为1,每次考虑在原数的末尾加上0或1,按照此法进行搜索,当构造的数%N==0时,即为答案。此处搜索要用广搜,因为广搜可以保证搜到的第一个数一定是最小的。
显然,如此搜索需要剪枝。这里给出剪枝的方法是:若有两个构造得到的数x,y,不妨设x<y。如果x%N=y%N,那么y不必继续拓展。
证明。设x=k1n+r,y=k2n+r。x拓展得到的数x1=10k1n+10r,x2=10k1n+10r+1,y拓展得到的数为y1=10k2n+10r,y2=10k2n+10r+1。可以发现,x1%n=y1%n,x2%n=y2%n。于是得到,x拓展到答案的步数与y拓展到答案的步数相等。那么y显然不可能成为最佳答案,我们只需要拓展较小的数x即可。
具体实现时,只需不断标记x%n,之后若拓展得到的y%n已经被标记,那么continue。
但又有另一个问题:M可能极大,超出long long的范围,高精度又太慢,那该如何记录构造出的数呢?
注意到,数x到答案的步数只与x除n的余数r有关(可以从上文x拓展得到的x1与x2除n的余数证明)。再看上文剪枝的方法,发现:每个除n的余数只对应单独一个构造的数。那么我们可以以余数r为下标,记录对应的数的构造路径(例如101由10拓展得,那么101的r指向10的r)。输出时一路往前回指,递归输出即可。
此解针对的数据规模为N≤106,此题的进阶版N≤109需要更强大的算法,我也不会……

Code

#include<cstdio>
#include<queue>
using namespace std;
const int N = 1e6 + 10;
struct node {
	int bit, pre;
} path[N];
int n;
bool vis[N];
queue<int> q;
void bfs() {
	int x, y;
	path[1] = (node){1, -1};
	vis[1] = 1;
	q.push(1);
	while (!q.empty()) {
		x = q.front(); q.pop();
		if (x == 0) return ;
		for (int i = 0; i <= 1; i++) {
			y = (x * 10 + i) % n;
			if (!vis[y]) {
				path[y] = (node){i, x};
				vis[y] = 1;
				q.push(y);
			}
		}
	}
}
void Print(int x) {
	if (path[x].pre != -1) Print(path[x].pre);
	printf("%d", path[x].bit);
}
int main() {
	scanf("%d", &n);
	bfs();
	Print(0);
	printf("\n");
	return 0;
}

标签:11,10,01,剪枝,51nod,题解,拓展,int
来源: https://www.cnblogs.com/Fisher-Y/p/15860378.html