【题意】给了n个数字,和一个数m,其中m的范围在5000以内,现在问你能否用这n个数字拼成一个数,使得数是m的倍数,并且要保证这个数最小!
【分析】由于要最小,排序是必要的,然后就是搜索了。想了很久都不能搞定这个问题,网查大牛博客了。才发现这个题真的劲啊,用了一个同于剪枝!具体如下:
- A=MX+R
- B=NX+R
- 假设A,B对于X的余数相同 那么
- (10*A+d[i])%x
- (10*B+d[i])%x
- 的意义是一样的,所以只有当余数没出现过的情况下才加入到搜索的队列中来 另外还有一个问题,就是可能是最后的答案出现很庞大的位数,如果这里用高精度就太麻烦了。我用的是静态指针。还有就是单独处理N=0的情况【具体实现】知道这个剪枝就可以做这道题,可以用一个结构体维护当前这个节点的个位数,余数,以及Pre,为了输出答案,维护这个静态指针,始终指向当前节点的首位置,所以递归顺序输出就行了。vis标记当前这个节点的余数是否入队,这里当然是bfs啦。而且考虑到输出,跟上大牛博客,写成结构体数组模拟bfs,不然输出真的不好整!
【补充】这题应该属于数学知识剪枝加bfs,很劲。
【AC代码】
using namespace std;
const int maxn = 5050;
struct node{
int ge,yu,pre;
node(){}
node(int ge,int yu,int pre):ge(ge),yu(yu),pre(pre){}
}que[maxn];
int n,m;
int digit[100];
bool vis[maxn];
void print_ans(node t)
{
if(t.pre!=-1)
{
print_ans(que[t.pre]);
printf("%d",t.ge);
}
}
void bfs()
{
int head,tail,r;
memset(vis,false,sizeof(vis));
bool fuck=false;
que[0]=node(0,0,-1);
head=0,tail=1;
while(head<tail)
{
node now = que[head];
node cur;
for(int i=0;i<n;i++)
{
r=(10*now.yu+digit[i])%m;
if(!vis[r]&&(now.pre!=-1||digit[i]>0))
{
vis[r]=true;
cur.ge=digit[i];
cur.yu = r;
cur.pre = head;
que[tail++]=cur;
if(r==0)
{
fuck=true;
print_ans(cur);
printf("\n");
break;
}
}
if(fuck)break;
}
head++;
if(fuck)break;
}
if(fuck==false)
{
printf("0\n");
}
}
int main()
{
while(~scanf("%d",&m))
{
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&digit[i]);
sort(digit,digit+n);
if(m==0)
{
puts("0");
}
else
{
bfs();
}
}
return 0;
}