【题意】给了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代码】

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
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 ***=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)
                {
                    ***=true;
                    print_ans(cur);
                    printf("\n");
                    break;
                }
            }
            if(***)break;
        }
        head++;
        if(***)break;
    }
    if(***==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;
}