ZOJ 1136 Multiple BFS 取模 POJ 1465

该题的关键是如果a%n = x;

则(a*10+b)%n == (x*10+b)%n。例如你要算123456789%3,你只需算((12345678%n)*10+9)%3。

想法是把给你的数字先排序,然后扩展,这样能保证你拓展出的数是以递增序列出现的。按示例所示,给你数字1,7,0,你可以拓展出10,11,17;70,71,77;

从拓展出的六个新的数,每个数又能拓展出M(这里的M为3)个。然后要优化处理,算所谓的剪枝吧。拓展出的数模n的余数等于原数的余数*10+增加的数,再模n.

如果新数的余数已经出现过,就不入队列了,因为该数没有被拓展的必要(已经有一个比它小的数用来拓展)。如果出现某数的余数为0,循环结束,输出该数。如果队列空了,证明一直没有拓展出新的余数,那么再拓展下去也不会有新的余数出现,这样证明不存在这样的数,输出0.

下面的代码可以过ZOJ,因为ZOJ给了10s的时间。但是POJ不能过,因为POJ的时间限制是1s.

View Code
 1 #include <cstdio>
 2 #include <queue>
 3 #include <string>
 4 #include <cstring>
 5 #include <iostream>
 6 #include <algorithm>
 7 using namespace std;
 8 int p[10];    //出现的数字按从小到大放在p中
 9 int N,M;
10 bool v[5005];   //记录出现过的余数
11 struct node
12 {
13     int res;  //该字符串的余数
14     string s;
15 };
16 string BFS()
17 {
18     queue<node> Q;
19     memset(v,false,sizeof(v));
20     for(int i=0; i < M; i++) //预处理
21     {
22         if(p[i] == 0) continue;  //0不入队列,不用0来拓展
23         node ss ;
24         ss.res = p[i]%N;
25         ss.s = ss.s + (char) (p[i]+'0');
26         if(ss.res == 0)  return ss.s;  //出现余数 0,返回
27         if(v[ss.res]) continue;  //该余数出现过
28         v[ss.res] = true;
29         Q.push(ss);  //入队列
30     }
31     while(!Q.empty())  //非空
32     {
33         node s ;
34         s = Q.front();
35         Q.pop();
36         for(int j=0; j < M; j++)  //每个pop出的数用来拓展出M个数
37         {
38             node t;
39             t.res = (s.res*10 + p[j])%N;
40             t.s = s.s+(char)('0'+p[j]);  //拓展的数直接在原数后加一位数
41             if(t.res == 0)                return t.s;  //出现余数0,返回
42             if(v[t.res])  continue;  //余数出现过,不入队列
43             v[t.res] = true;  //新余数,标记为true
44             Q.push(t);
45         }
46     }
47     return "0";
48 }
49 int main()
50 {
51     while(scanf("%d",&N) != EOF)
52     {
53         scanf("%d",&M);
54         for(int i= 0 ; i < M; i++)
55             scanf("%d",&p[i]);
56         if(N==0)  //特判0
57         {
58             printf("0\n");
59             continue;
60         }
61         sort(p,p+M);  //排序
62         cout<<BFS()<<endl;
63     }
64     return 0;
65 }

 因为上面在保存被拓展出来的数时整个数都被保存了,事实上是没有必要的,只需要记录这个数的前一位是谁就行了,比如4535,我不需要保存4535,我只要知道5的前面是3,3的前面是5,这个5的前面是4,而4535是由453拓展出来的,453是由45拓展出来的,45是由4拓展出来的。这样就能节省内存和时间了,就像记录路径一样,给每个被拓展的数除了有个编号,还添加int pre,和int now, now表明拓展时我加的数字,pre表示是由哪个数字拓展出来的,那么该数字的now值就是我的前一位了。回到上面的例子,数字4535的now值是5,pre值是数字453的编号,数字453的now值是3,pre值是45的编号,以此类推。

贴代码:

View Code
 1 #include <cstdio>
 2 #include <string>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <algorithm>
 6 using namespace std;
 7 int p[10];    //出现的数字按从小到大放在p中
 8 int N,M;
 9 bool v[5005];   //记录出现过的余数
10 struct node
11 {
12     int res;  //该字符串的余数
13     int pre;
14     char now;
15 };
16 void BFS()
17 {
18     node Q[6000];
19     int head=0,tail=0;
20     memset(v,false,sizeof(v));
21     for(int i=0; i < M; i++) //预处理
22     {
23         if(p[i] == 0) continue;  //0不入队列,不用0来拓展
24         node  ss ;
25         ss.res = p[i]%N;
26         ss.pre = -1;
27         ss.now = p[i];
28         if(ss.res == 0)  //出现余数 0,返回
29         {
30             printf("%d\n",p[i]);
31             return ;
32         }
33         if(v[ss.res]) continue;  //该余数出现过
34         v[ss.res] = true;
35         Q[tail] = ss;
36         tail++;
37     }
38     while(head != tail)  //非空
39     {
40         node s ;
41         s= Q[head] ;
42         for(int j=0; j < M; j++)  //每个pop出的数用来拓展出M个数
43         {
44             node t;
45             t.res = (s.res*10 + p[j])%N;
46             t.pre = head;  //拓展的数直接在原数后加一位数
47             t.now = p[j] ;
48             if(t.res == 0)        //出现余数0,返回
49             {
50                 int cur = 0;
51                 node re  = t;
52                 char d[1000];
53                 while(re.pre !=-1)
54                 {
55                     d[cur++] = re.now;
56                     re = Q[re.pre];
57                 }
58                 d[cur++] = re.now;
59                 for(int i=cur-1; i>=0; i--)
60                     printf("%d",d[i]);
61                 printf("\n");
62                 return ;
63             }
64             if(v[t.res])  continue;  //余数出现过,不入队列
65             v[t.res] = true;  //新余数,标记为true
66             Q[tail] = t;
67             tail++;
68         }
69         head++;
70     }
71     printf("0\n");
72     return ;
73 }
74 int main()
75 {
76     while(scanf("%d",&N) != EOF)
77     {
78         scanf("%d",&M);
79         for(int i= 0 ; i < M; i++)
80             scanf("%d",&p[i]);
81         if(N==0)  //特判0
82         {
83             printf("0\n");
84             continue;
85         }
86         sort(p,p+M);  //排序
87         BFS();
88     }
89     return 0;
90 }
原文地址:https://www.cnblogs.com/allh123/p/2979401.html