通过几道经典BFS例题阐述BFS思路
ZOJ2913-Bus Pass
题意:找一个center区域,使得center到所有公交线路最短,有等距的center则输出id最小的。
题解:经典的BFS,由公交线路最多只经过10*20个区域,而总区域数可达10^5个,因此应该从公交线路通过队列一层层向外扩展,最后判断一次center的位置即可。
1 //选定一个center使得其到所有公交线路最短 2 //对所有公交线路进行BFS(公交线路比其他区域少得多) 3 //Time:150Ms Memory:2840K 4 #include<iostream> 5 #include<cstring> 6 #include<cstdio> 7 #include<queue> 8 #include<vector> 9 using namespace std; 10 11 #define MAX 10005 12 #define max(x,y) ((x)>(y)?(x):(y)) 13 14 int nz, nr; 15 int td[MAX]; //临时距离记录-并充当已访问记录 16 int d[MAX]; //总距离记录 17 bool trip[MAX]; //记录所有公交线路 18 vector<int> zones[MAX]; //邻接表 19 20 void bfs(int x) 21 { 22 memset(td, 0, sizeof(td)); 23 queue<int> qz; //queue_zones 24 td[x] = 1; 25 qz.push(x); 26 while (!qz.empty()) 27 { 28 int cur = qz.front(); 29 qz.pop(); 30 for (int i = 0; i < zones[cur].size(); i++) 31 { 32 int adj = zones[cur].at(i); 33 if (!td[adj]) { 34 td[adj] = max(td[adj], td[cur] + 1); 35 qz.push(adj); 36 } 37 } 38 d[cur] = max(d[cur], td[cur]); 39 } 40 } 41 42 int main() 43 { 44 int T; 45 scanf("%d", &T); 46 while (T--) 47 { 48 //Init 49 memset(trip, false, sizeof(trip)); 50 memset(d, 0, sizeof(d)); 51 memset(zones, 0, sizeof(zones)); 52 53 //Input 54 scanf("%d%d", &nz, &nr); 55 for (int i = 0; i < nz; i++) 56 { 57 int id, adj; //adjacent 58 scanf("%d%d", &id, &adj); 59 while (adj--) { 60 int num; 61 scanf("%d", &num); 62 zones[id].push_back(num); 63 } 64 } 65 for (int i = 0; i < nr; i++) 66 { 67 int id,sum; 68 scanf("%d", &sum); 69 while (sum--) 70 { 71 scanf("%d", &id); 72 if (!trip[id]) { //未遍历过 73 trip[id] = true; //记录为公交线路 74 bfs(id); 75 } 76 } 77 } 78 79 //Output 80 int minstep = MAX; 81 int minid; 82 for (int i = 0; i < MAX; i++) 83 if (d[i] && minstep > d[i]) 84 minstep = d[minid = i]; //保证id最小+最小距离 85 86 printf("%d %d\n", minstep, minid); 87 } 88 89 return 0; 90 }
ZOJ1136(POJ1465)-Multiple
题意:找出m个十进制数字所组成的n的倍数的最小值
题解:依次枚举每一位上的数字进行BFS搜索,并利用初等数论中同余的性质进行剪枝。
现在阐述有关同余剪枝的算法:
如果有两个数a,b = n*k+c (k为任意整数),即a%n = b%n = c
假定n = 11,a = 24,b = 35,此时c=2
如果枚举的下一位数字为2,则a = 242,刚好是n的倍数,而b = 352也刚好是n的倍数
这样的情况不是偶然,对于普遍情况 a,b = n*k + c (k为任意整数)
恒有(a*10 + m)%n == (b*10 + m)%n
即:增加一位数后,n的同余数依然是同余数
因此在进行BFS时,只需要对“所有同余数的最小值”考虑即可。
1 //给出n,求能使所给的数字组成n的倍数的最小值 2 //BFS+数论(同余剪枝) 3 //Time:47Ms Memory:184K 4 #include<iostream> 5 #include<cstring> 6 #include<cstdio> 7 #include<algorithm> 8 using namespace std; 9 #define MAXN 5000 10 struct NUM { 11 int dig; 12 int rem; 13 int fa; //父节点 14 }num[MAXN]; 15 int n, m; 16 int dig[10]; 17 bool v[MAXN]; //余数记录 18 //递归输出 19 void output(int x) 20 { 21 if (num[x].fa) 22 output(num[x].fa); 23 printf("%d", num[x].dig); 24 } 25 void bfs() 26 { 27 memset(v, false, sizeof(v)); 28 int front = 0, tail = 0; 29 num[0].rem = num[0].dig = 0; 30 num[0].fa = NULL; 31 while (front <= tail) 32 { 33 NUM t; 34 t.fa = front; 35 for (int i = 0; i < m; i++) //逐次枚举下一个数字 36 { 37 t.rem = (num[front].rem * 10 + dig[i]) % n; 38 if (!v[t.rem] && (t.rem || front)) //排除同余数 39 { 40 v[t.rem] = true; 41 t.dig = dig[i]; 42 num[++tail] = t; 43 if (t.rem == 0) 44 { 45 output(tail); 46 printf("\n"); 47 return; 48 } 49 } 50 } 51 front++; 52 } 53 54 printf("0\n"); 55 } 56 int main() 57 { 58 while (scanf("%d%d", &n, &m) != EOF) 59 { 60 for (int i = 0; i < m; i++) 61 scanf("%d", &dig[i]); 62 if (n == 0) { //n=0的时候要么不存在此倍数,要么就为0,因此可直接输出0 63 printf("0\n"); 64 continue; 65 } 66 sort(dig, dig + m); 67 bfs(); 68 } 69 return 0; 70 }