题目: uva 1653
题目大意: 给出一个数字n,和m个数字
要求求出在不含有 这m个数字的情况下,n的最小倍数 !
思路
利用BFS 算法,模拟除法操作,还原出相应的倍数!
大致操作
起初在 BFS队列中 推入一个0,表示除法操作中的一次余数
根据模拟除法操作,余数会*10 + 被除数下一位的数字(0-9枚举) 组成一个新的被除数
然后新被除数 % n 得到新余数
当新余数为0时,代表 我们枚举的数字组成了 n的倍数
即为答案
如图
先用 8%7 会得到余数 1
余数 1会 *10 然后 加上 8 的下一位数字(0) 组成一个新的被除数 10
枚举答案
我们通过枚举 0-9的十个数字 一位一位的构建 n的倍数
当然不能 使用那些被禁止的数字
并且当余数为0时且枚举到0时 这种情况应该直接跳过
因为如果枚举到0,且余数为0,下一次余数一定是0,我们不能选择0
当作答案,所以这种情况应当直接跳过!
通过枚举 0-9数字,一位一位的 对 被除数进行 补充
因为我们要寻找最小的倍数,所以一旦再次出现 余数为0的情况,代表这就是n的最小倍数
即为找到答案,退出 BFS,然后输出答案!
剪枝细节
用vis数组进行剪枝,防止计算重复的情况,对每一次余数进行标记
如果出现重复,则直接跳过!
当余数重复时,就是重复的情况
例如 11%7=4, 18%7=4,由于我们的最终目标是得到余数0
那么再接下来的计算中 被除数=4*10+ 接下来我们要枚举的数字
二者的被除数是完全相同的,所以接下来对于我们计算出的余数过程也是完全一致的,所以就需要用vis对所有的余数进行标记,防止重复计算相同情况!
记录答案与输出答案
用text 数组记录到达这个余数所枚举的相应的数字
(text数组记录的就是答案的每一位)
用pre数组记录到达这个余数的上一个余数
text[yushu]=i;
pre[yushu]=cur;
这样我们就可以通过 逆向递推的方式 将答案构建出来!
由于最后一次余数一定为0
第一次的余数也为0
所以有如下推理
int p=0;(最后一次的余数)
while()
{
ans[k++]=text[p];(记录答案)
p=pre[p];(寻找上一个余数)
}
当p再一次为0时,跳出循环,反向输出ans数组即可!
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
bool vis[N];
bool ban[N];
int pre[N];
int text[N];
int n,m;
bool bfs()
{
queue<int> Q;
Q.push(0);
int cur;
while(Q.size())
{
cur=Q.front();
Q.pop();
//cout<<cur<<endl;
for(int i=0;i<=9;i++)
{
if(ban[i]==1||(cur==0&&i==0))
{
continue;
}
//如果这个数字不被禁止!
int yushu=(cur*10+i)%n; // //模拟除法!!!! ///
if(vis[yushu]==1)
continue;
text[yushu]=i;//2 //8
pre[yushu]=cur;//0 //2;
vis[yushu]=1;
Q.push(yushu);
if(yushu==0)
return 1;
}
}
return 0;
}
void print()
{
int p=0,k=0;
int ans[N];
while(p!=0||k==0)
{
ans[k++]=text[p];//text[0]=8, text[2]=2, 28
p=pre[p];// p=pre[0]=2 , p=pre[2]=0;
}
for(int i=k-1;i>=0;i--) //倒序输出// 82 即为 28!!!!!!
cout<<ans[i];
cout<<endl;
}
int main()
{
int kase=1;
while(cin>>n>>m)
{
memset(vis,0,sizeof(vis));
memset(ban,0,sizeof(ban));
for(int i=1;i<=m;i++)
{
int x;
cin>>x;
ban[x]=1;
}
printf("Case %d: ",kase++);
if(bfs()==0)
printf("-1\n");
else
print();
}
return 0;
}