【题意】给了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 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;
    }