hdu4474 2012 Asia Chengdu Regional Contest 数位bfs+同余剪枝+模数化约

 1 #include<iostream>
 2 #include<string.h>
 3 #include<stdio.h>
 4 using namespace std;
 5 struct Node
 6 {
 7     int r;//余数
 8     int f;//父亲节点
 9     int n;//数字
10     Node(){};
11     Node(int r1,int f1,int n1){r=r1;f=f1;n=n1;}
12 }node[10010<<2];
13 int d[11];
14 bool rem[11000];
15 int n,m;
16 void print(int f)
17 {
18     if (node[f].f!=-1) print(node[f].f);
19     printf("%d",node[f].n);
20 //    if ( f == -1 ) return;
21 //    print( node[f].f );
22 //    printf( "%d", node[f].n );
23 }
24 int bfs()
25 {
26     memset(rem,0,sizeof(rem));
27     int head=1;int tail=0;
28     for(int i=1;i<=9;i++)//首位不可以为0;
29     {
30         if(d[i]) continue;
31         int r=i%n;
32         if(rem[r]) continue;
33         node[++tail]=Node(r,-1,i);rem[r]=true;
34         if (r==0) return tail;
35     }
36     while(head<=tail)//一开始等号没写,一直w
37     {
38         Node k=node[head];
39         for(int i=0;i<10;i++)
40         {
41             if (d[i]) continue;
42             int r=(k.r*10+i)%n;
43             if (rem[r]) continue;
44             node[++tail]=Node(r,head,i);
45             rem[r]=true;
46             if (r==0) return tail;
47         }
48         head++;
49     }
50     return -1;
51 }
52 int main()
53 {
54     int cas=0;
55     while(~scanf("%d%d",&n,&m))
56     {
57         memset(d,0,sizeof(d));
58         for(int i=0;i<m;i++)
59         {
60             int k;
61             scanf("%d",&k);
62             d[k]=1;
63         }
64         printf("Case %d: ",++cas);
65         int ans=bfs();//找到答案则返回最后底层节点的序号
66         if (ans==-1) printf("-1\n");else {print(ans);printf("\n");}
67     }
68     return 0;
69 }
View Code

题目描述:给定一个数n,用0--9中的某几个数字组成任意正整数m,求解使m%n==0的最小的m,若m不存在,则输出-1.

方法:

1、暴力枚举n,2n,3n。。。判断是否由给定的几个数字组成,满足则输出。防止超时,限制枚举步数。

   分析:使用高精度,编码较复杂;枚举步数一直TLE或W,说明这个算法行不通

2、标准方法:按数字从小到大bfs+bfs父节点存储+记忆化搜索记录简化余数计算+同余剪枝(最后的这个是剪枝的关键啊!!!)

3、方法分述:

    (1)bfs()

     例如:用1、2、3 组成数

     拓扑结构:第一层:      1            2                 3

                   第二层:   1   2   3    1  2  3      1     2    3

    说穿了就是个满n叉树。。。。。

    有了拓扑结构图,我们建模就思路清晰了。

     (2)记录父节点

    开始我是用queue容器+指针的写法,实在是一直错,所以我就手写了"指针",以数组下标为地址,一一对应,这点在《入门经典》的图论一部分讲的很透彻,让我又复习了一遍。

     (3)记忆化搜索余数

      设父节点f,余数mf;子节点c,余mc,c的个位数为cn

      则 mc=(mf+cn)%n;这显然比高精度求余要优化指数级别= =

      (4)同余剪枝

      这个就属于数论里面的内容,描述如下:(waiting 拓展)

4、代码描述:

    (1)使用了手写队列

    (2)注意 head<=tail 这个条件,在整个出入队列的过程中

一个同样的数位bfs的问题:http://www.cnblogs.com/bvbook/archive/2009/02/06/1385448.html

posted @ 2013-10-31 12:51  little_w  阅读(188)  评论(0编辑  收藏  举报