题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1226
题目大意:求一个数,它是由M个数字组成,是C进制,且是N(10进制整数)的整数倍。求这个数的最小值。
例如N=22,C=10,M=3,三个数字是7,0,1;那么满足由7,0,1组成的10进制数且是22的倍数的最小值是110。
由于求的数是最小的,用bfs比dfs更好一些。用bfs实现由数字组成的数按从小到大放入队列中,如果满足题目要求的条件,就输出并退出。密码最多有500位,必须用数组存储这个数。在判断这个数是否是N的倍数时,要用剩余定理。
(a+b)%n = a%n + b%n
例如我想判断16进制的数CCB是否是10进制数25的倍数(C是12,B是11)我们从高位向低位依次计算:
(12*16+12)%25 = 4
(4*16+11)%25 = 0
所以CCB是25的倍数。
剪枝:因为通过bfs产生的数是从小到大产生的,所以如果两个数(两个数肯定是一大一小)取模的余数相等了话,在以后的bfs中这两个数的变化是相同的,而题目求的是最小的数,较大的数就不用再进行bfs了。
#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
using namespace std;
int digit[20];
int vis[5010];
int T,N,C,M;
struct node
{
int num[510];
int len;
};
int judge(node p)
{
int t = 0;
for(int i=0; i<p.len; i++)
t = (t * C +p.num[i]) % N;
return t;
}
int bfs()
{
queue<node>Q;
node p,q;
p.len = 0;
Q.push(p);
while(!Q.empty())
{
p = Q.front();
Q.pop();
for(int i = (p.len==0 ? 1 : 0); i<16; i++)
{
if(digit[i] && p.len<=500)
{
q = p;
q.num[q.len] = i;
q.len++;
if(judge(q)==0)
{
for(int i=0; i<q.len; i++)
printf("%X",q.num[i]);
cout<<endl;
return 1;
}
if(!vis[judge(q)])
{
vis[judge(q)] = 1;
Q.push(q);
}
}
}
}
return 0;
}
int main()
{
cin>>T;
while(T--)
{
cin>>N>>C>>M;
memset(digit,0,sizeof(digit));
memset(vis,0,sizeof(vis));
int t;
for(int i=0; i<M; i++)
{
scanf("%x",&t);
digit[t] = 1;
}
if( N==0 && digit[0] ) //只有0才是0的正整数倍
cout<<0<<endl;
else if( N==0 && !digit[0] ) //如果构成的数字没有0,则没有0这个密码构成
cout<<"give me the bomb please"<<endl;
else if(!bfs())
cout<<"give me the bomb please"<<endl;
}
return 0;
}