题目链接:超级密码
题目大意:找出最小的一个数,满足由给定的
(1)m个数字组成(有些数字可以不用)
(2)可以整除一个数n
由于最多可以有500位数,每个数字最多有16种情况,单纯暴力肯定会TLE.
--》bfs
用bool num[16]记录给出的m个数字,一步步遍历
由于数字位数大,用大了大数求余
int mod(const node &cur)
{
int tmp=0;
for(int i=0;i<cur.len;i++){
tmp=(tmp*c+cur.s[i])%n;
}
return tmp;
}
剪枝技巧:
A%X == B%X (设A<B)
那么 (A*10+Ki)%X==(B*10+Ki)%X
所以A,B之中我们只要取前面的A就行,因为题目要取最小的数,通过同余我们可以知道,A和B这两个数在末尾添加任何相同的数MOD X之后的余数都是一样的,因此对于比A大的数我们就没有必要去扩展了。
#include <stdio.h>
#include <queue>
#include <memory.h>
#include <algorithm>
using namespace std;
struct node
{
int s[505];
int len;
};
int c,n,m,maxm;
bool num[20];
bool vis[5005];
void init()
{
maxm=-1;
memset(num,0,sizeof(num));
memset(vis,0,sizeof(vis));
}
void print(const node &cur)
{
for(int i=0;i<cur.len;i++){
if(cur.s[i]<=9)printf("%d",cur.s[i]);
else printf("%c",cur.s[i]-10+'A');
}
printf("\n");
}
int mod(const node &cur)
{
int tmp=0;
for(int i=0;i<cur.len;i++){
tmp=(tmp*c+cur.s[i])%n;
}
return tmp;
}
void bfs()
{
int r;
queue<node>q;
node cur;
for(int i=1;i<=maxm;i++){
if(num[i]){
cur.s[0]=i;
cur.len=1;
if((r=mod(cur))==0){
print(cur);
return;
}
else if(!vis[r]){
vis[r]=true;
q.push(cur);
}
}
}
while(!q.empty()){
cur=q.front();
q.pop();
for(int i=0;i<=maxm;i++){
if(num[i]){
cur.s[cur.len]=i;
cur.len++;
if((r=mod(cur))==0){
print(cur);
return;
}
else if(!vis[r]&&cur.len<499){
vis[r]=true;
q.push(cur);
}
cur.len--;
}
}
}
printf("give me the bomb please\n");
}
int main()
{
//freopen("in.txt","r",stdin);
int T,tmp;
char ch[2];
scanf("%d",&T);
while(T--){
init();
scanf("%d %d",&n,&c);
scanf("%d",&m);
for(int i=0;i<m;i++){
scanf("%s",&ch);
if(ch[0]<='9') tmp=ch[0]-'0';
else tmp=ch[0]-'A'+10;
num[tmp]=true;
maxm=max(maxm,tmp);
}
if(n==0){
if(num[0])printf("0\n");
else printf("give me the bomb please\n");
}
else bfs();
}
return 0;
}