题目: 给一个数N,N>= 0, N < 5000, 然后给出m个数,求N的最小倍数,这个倍数比一定是有这m个数组成,比如N=12, m个数里面有1,2, 那么12是N的最小倍数,是有m个数字中的1和2合成的。注意,m中的每个数字是可以重复使用的。
解析:
第一,分析可知,由于它是加的位数,而不是数字,所以,对于数一定要有处理,不可能盲目的追加位数!
第二,这m个数,根据题意,可以组成的数字是无穷多的
所以,这里需要一个解决的办法,那就是取余。事实上,任何一个数对n取余,一共只能有n个结果(如果算0的话)。而且有这样的一个式子,如果x%n == y%n, 那么x%n * 10 + c == y%n *10 + c,由此可见,我们只要判断这个余数即可,将所得的余数和剩下的m个数组和,如果得到的数对n取余的余数之前组和过,那么就不需要再进行判断了,因为,之前已经判断过了。也就是说,这里和m个数是谁不重要,重要的是,这个余数,一旦这m个数组和成的数。
接下来说一下大致的算法:
首先将m个数从下到大排序,这样在bfs的过程中也是从小到大遍历,保证结果最小。
然后,开始bfs,建立一个vis数组,来记录n的那些已经遍历过的余数,队列里面存储的也是余数,然后从小到大遍历。比较重要的是记得建立一个父节点数组,用来输出。
最后要注意的是,对于父节点数组,要指向的节点是自己新建立的,而不是那m个数,这个很容易想到。
解释一下代码:结构体NODE里面,存储这个点的id,余数,父节点,和对应的数。bfs判断是否存在一个这个倍数,outprintf递归输出。bfs中,第一个for循环, 算是用来初始化的,有一点要注意的是,0不能加入到队列里面,因为求的是最小正整数。然后后面的那个while就正常做了。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <set>
#include <list>
#include <queue>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define maxn 70
#define mem(a) memset(a,0,sizeof(a))
const double eps = 1e-10;
struct node
{
string num;
int mod;
node(){}
node(string a,int b)
{
num = a;
mod = b;
}
}tmp;
int n,m,a[11],vis[50005];
string ans;
int solve()
{
queue<node> q;
q.push(node("",0));
mem(vis);
while(!q.empty())
{
node p = q.front();
q.pop();
for(int i = 0; i < m; i++)
{
tmp.num = p.num + char('0'+a[i]);
tmp.mod = (p.mod*10 + a[i]) % n;
if(tmp.num != "0" && tmp.mod == 0)
{
ans = tmp.num;
return true;
}
else if(vis[tmp.mod] == 0 && tmp.num != "0")
{
q.push(tmp);
vis[tmp.mod] = 1;
}
}
}
return 0;
}
int main()
{
int t,c = 0;
//scanf("%d",&t);
while(scanf("%d%d",&n,&m) != EOF)
{
int flag = 0;
for(int i = 0; i < m; i++)
{
scanf("%d",&a[i]);
}
sort(a,a+m);
if(!n)
printf("0\n");
else if(solve())
cout << ans<<endl;
else
printf("0\n");
}
return 0;
}