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.
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的编号,以此类推。
贴代码:
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 }