通過幾道經典BFS例題闡述BFS思路
ZOJ2913-Bus Pass
題意:找一個center區域,使得center到所有公交線路最短,有等距的center則輸出id最小的。
題解:經典的BFS,由公交線路最多隻經過10*20個區域,而總區域數可達10^5個,因此應該從公交線路通過佇列一層層向外擴充套件,最後判斷一次center的位置即可。
//選定一個center使得其到所有公交線路最短
//對所有公交線路進行BFS(公交線路比其他區域少得多)
//Time:150Ms Memory:2840K
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std; #define MAX 10005
#define max(x,y) ((x)>(y)?(x):(y)) int nz, nr;
int td[MAX]; //臨時距離記錄-並充當已訪問記錄
int d[MAX]; //總距離記錄
bool trip[MAX]; //記錄所有公交線路
vector<int> zones[MAX]; //鄰接表 void bfs(int x)
{
memset(td, , sizeof(td));
queue<int> qz; //queue_zones
td[x] = ;
qz.push(x);
while (!qz.empty())
{
int cur = qz.front();
qz.pop();
for (int i = ; i < zones[cur].size(); i++)
{
int adj = zones[cur].at(i);
if (!td[adj]) {
td[adj] = max(td[adj], td[cur] + );
qz.push(adj);
}
}
d[cur] = max(d[cur], td[cur]);
}
} int main()
{
int T;
scanf("%d", &T);
while (T--)
{
//Init
memset(trip, false, sizeof(trip));
memset(d, , sizeof(d));
memset(zones, , sizeof(zones)); //Input
scanf("%d%d", &nz, &nr);
for (int i = ; i < nz; i++)
{
int id, adj; //adjacent
scanf("%d%d", &id, &adj);
while (adj--) {
int num;
scanf("%d", &num);
zones[id].push_back(num);
}
}
for (int i = ; i < nr; i++)
{
int id,sum;
scanf("%d", &sum);
while (sum--)
{
scanf("%d", &id);
if (!trip[id]) { //未遍歷過
trip[id] = true; //記錄為公交線路
bfs(id);
}
}
} //Output
int minstep = MAX;
int minid;
for (int i = ; i < MAX; i++)
if (d[i] && minstep > d[i])
minstep = d[minid = i]; //保證id最小+最小距離 printf("%d %d\n", minstep, minid);
} return ;
}
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時,只需要對“所有同餘數的最小值”考慮即可。
//給出n,求能使所給的數字組成n的倍數的最小值
//BFS+數論(同餘剪枝)
//Time:47Ms Memory:184K
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 5000
struct NUM {
int dig;
int rem;
int fa; //父節點
}num[MAXN];
int n, m;
int dig[];
bool v[MAXN]; //餘數記錄
//遞迴輸出
void output(int x)
{
if (num[x].fa)
output(num[x].fa);
printf("%d", num[x].dig);
}
void bfs()
{
memset(v, false, sizeof(v));
int front = , tail = ;
num[].rem = num[].dig = ;
num[].fa = NULL;
while (front <= tail)
{
NUM t;
t.fa = front;
for (int i = ; i < m; i++) //逐次列舉下一個數字
{
t.rem = (num[front].rem * + dig[i]) % n;
if (!v[t.rem] && (t.rem || front)) //排除同餘數
{
v[t.rem] = true;
t.dig = dig[i];
num[++tail] = t;
if (t.rem == )
{
output(tail);
printf("\n");
return;
}
}
}
front++;
} printf("0\n");
}
int main()
{
while (scanf("%d%d", &n, &m) != EOF)
{
for (int i = ; i < m; i++)
scanf("%d", &dig[i]);
if (n == ) { //n=0的時候要麼不存在此倍數,要麼就為0,因此可直接輸出0
printf("0\n");
continue;
}
sort(dig, dig + m);
bfs();
}
return ;
}