题目链接

BFS+同余

以余数作为状态做BFS,目标状态为余数为0

假设存在两个数A,B对N取余相同,且A<B,那么A和B同时加或者乘同一个数得到的两个数对N取余仍然是相同的,由此能得出的结论是,假设A和B作为状态做BFS,B能得到的余数,A也一定能得到,我们要求的目标状态是余数为0,那么如果队列里面已经有了一个余N为x的数,如果当前的数余N也为x,那么当前这个数就可以不要,这样能避免重复遍历状态。

题目要求的倍数的位数可能很大,所以需要给每一个节点设置一个指向前驱的变量,每一个节点只要记录当前位的位数,通过前驱能得到BFS的路径,由此就能得到整个倍数的值。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <vector>
#include <map>
#include <cstdlib>
#include <algorithm>
#include <cmath>

using namespace std;

const int Maxn = 10000+10;
const int INF = 0x3f3f3f3f;

struct Node {
	int pre, r, dig;
} qu[Maxn];

int d[Maxn], n, m, head, tail, a[Maxn];
char S[Maxn];
bool vis[5500];

int main(void)
{
	while(scanf("%d", &n) != EOF) {
		scanf("%d", &m);
		for(int i = 0; i < m; ++i) scanf("%d", &d[i]);
		sort(d, d+m);
		if(n == 0) printf("0\n");
		else {
			head = tail = 0;
			Node tmp;
			memset(vis, false, sizeof(vis));
			for(int i = 0; i < m; ++i) {
				if(d[i] == 0) continue;
				tmp.r = d[i]%n;
				tmp.pre = -1;
				tmp.dig = d[i];
				vis[tmp.r] = true;
				qu[tail++] = tmp;
			} 
			bool ok = false;
			while(head < tail) {
				tmp = qu[head++];
				
				if(tmp.r == 0) {
					ok = true;
					head--;
					break;
				}
				
				for(int i = 0; i < m; ++i) {
					Node cur = tmp;
					cur.r = (cur.r*10+d[i]) % n;
					if(!vis[cur.r]) {
						vis[cur.r] = true;
						cur.pre = head-1;
						cur.dig = d[i];
						qu[tail++] = cur;
					}
				}
			}
			if(!ok) printf("0\n");
			else {
				int cnt = 0;
				for(int i = head; i != -1; i = qu[i].pre) a[cnt++] = qu[i].dig;
				int ct = 0;
				for(int i = cnt-1; i >= 0; --i) S[ct++] = a[i]+'0';
				S[ct] = '\0';
				puts(S);
			}
		}
	}
	return 0;
 }