传送门:hdu1226
中文题题意就不多说了,重点是方法,不要试图去枚举十进制n的倍数来然后看看给的几个数字能否凑出来,可以想象2要翻倍多少次才能达到500位的长度,因此要从给定的数字入手,枚举数字的各种组合情况,判断是不是n的整数倍。而且这样的话还有一个强剪枝就是如果余数相同就没必要再次入队了,因为无论是大数还是小数如果模n同余那么再添上其他数字的话再模n还是同余的,这样每个余数最多在队列里出现一次,也就是说队列中最多出现5000个结点,大大优化了时间。
还有要注意的一点是n=0时的特判。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
int n,c,m,book[20],vis[5005];
int flag=0;
struct node{ //结构体中的数组用来存密码的每一位
int s[505],len;
};
queue<node> q;
int mod(node t)//取余函数
{
int temp=0;
for(int i=0;i<t.len;i++)
{
temp=(temp*c+t.s[i])%n;
}
return temp;
}
void print(node a)
{
for(int i=0;i<a.len;i++)
{
if(a.s[i]<=9)
printf("%d",a.s[i]);
else
printf("%c",a.s[i]+'A'-10);
}
putchar('\n');
return ;
}
void bfs()
{
node t;
t.len=0;
int v;
for(int i=1;i<16;i++)//先处理第一个结点,注意这里是从一开始循环,因为密码第一位不能为零
{
if(book[i])
{
t.s[0]=i;
t.len=1;
v=mod(t);
if(!v)
{
print(t);
flag=1;
return ;
}
else
{
if(!vis[v])
{
vis[v]=1;
q.push(t);
}
}
}
}
while(!q.empty())
{
t=q.front();
q.pop();
for(int i=0;i<16;i++)
{
if(book[i])
{
t.s[t.len]=i;
t.len++;
v=mod(t);
if(!v)
{
print(t);
flag=1;
return ;
}
else
{
if(!vis[v]&&t.len<499)//注意这里要判断一下位数再入队
{
vis[v]=1;
q.push(t);
}
}
t.len--;
}
}
}
return ;
}
int main()
{
int T;
char s[2];
scanf("%d",&T);
while(T--)
{
flag=0;
memset(vis,0,sizeof(vis));
memset(book,0,sizeof(book));
while(!q.empty())
q.pop();
scanf("%d%d%d",&n,&c,&m);
getchar();
for(int i=0;i<m;i++)
{
scanf("%s",s);
if(s[0]<='9'&&s[0]>='0')
book[s[0]-'0']=1;
else
{
book[10+s[0]-'A']=1;
}
}
/*for(int i=0;i<16;i++)
printf("%d",book[i]);*/
if(n)//n等于0的话不能进行取模操作因此也不能入队,以为0的倍数只有0所以特判一下给没给0这个数字就好了
{
bfs();
if(!flag)
printf("give me the bomb please\n");
}
else
{
if(book[0])
printf("0\n");
else
printf("give me the bomb please\n");
}
}
return 0;
}