ACM/ICPC 之 BFS范例(ZOJ2913-ZOJ1136(POJ1465))

通过几道经典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 }

 

posted @ 2016-03-20 10:53  文字失效  阅读(268)  评论(0编辑  收藏  举报