經過幾道經典BFS例題闡述BFS思路ios
ZOJ2913-Bus Pass算法
題意:找一個center區域,使得center到全部公交線路最短,有等距的center則輸出id最小的。spa
題解:經典的BFS,由公交線路最多隻通過10*20個區域,而總區域數可達10^5個,所以應該從公交線路經過隊列一層層向外擴展,最後判斷一次center的位置便可。code
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)-Multipleblog
題意:找出m個十進制數字所組成的n的倍數的最小值遞歸
題解:依次枚舉每一位上的數字進行BFS搜索,並利用初等數論中同餘的性質進行剪枝。隊列
如今闡述有關同餘剪枝的算法:ip
若是有兩個數a,b = n*k+c (k爲任意整數),即a%n = b%n = crem
假定n = 11,a = 24,b = 35,此時c=2string
若是枚舉的下一位數字爲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 }