关于bfs代码里面已经详细注释了,对于这个题,个人理解是除了双向bfs加深了理解以外,更需要注意的是隐式图的建立和一种直觉吧。怎样能看出隐式图,和如果建立,还需要多磨砺。
#include<iostream>
#include<queue >
#include<algorithm>
#include<cstring>
using namespace std;
int w, h, n;
int s[3], t[3]; //表示初态和末态的状态。
char data1[20][20];
int g[200][5];//每个点有5种移动的方式。
int color[200][200][200], dist[200][200][200]; //后面解释
int deg[200];//记录每个格子有多少种可以走的方式。
int dx[] = { 0,-1,1,0,0 };
int dy[] = { 0,0,0,1,-1 };
inline int ID(int a, int b, int c) {
return (a << 16) | (b << 8) | c; //a,b,c分别是其各自所在空格的编号,编号要注意方法,比如。12,7,5,可以变成一个整数1275,但是如何把1275变回来 就不好确定了。
} // 所以要掌握好这种二进制压缩的方法
inline bool conflict(int a, int b, int a1, int b1) {
return ((a1 == b1) || (a == b1) && (b == a1)); //第一个表示不能重叠位置,第二个表示不能交换位置。
}
int bfs() {
queue<int> qf;
queue<int> qb;
dist[s[0]][s[1]][s[2]] = 0; //标记走到该状态需要多少步
dist[t[0]][t[1]][t[2]] = 0;
qf.push(ID(s[0], s[1], s[2]));
qb.push(ID(t[0], t[1], t[2]));
color[s[0]][s[1]][s[2]] = 1; //0表示 没有走过,所以用1.2标记
color[t[0]][t[1]][t[2]] = 2;
bool is;
while (!qf.empty() && !qb.empty()) {
int num, u;
if (qf.size() < qb.size() || qb.empty()) { //找结点少的开始bfs。
num = qf.size();
is = true;
}
else {
num = qb.size();
is = false;
}
while (num--) {
if (is) {
u = qf.front();
qf.pop();
}
else {
u = qb.front();
qb.pop();
}
int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u & 0xff;
for (int i = 0; i < deg[a]; ++i) {
int a1 = g[a][i];
for (int j = 0; j < deg[b]; ++j) {
int b1 = g[b][j];
if (conflict(a, b, a1, b1)) continue;
for (int k = 0; k < deg[c]; ++k) {
int c1 = g[c][k];
if (conflict(a, c, a1, c1) || conflict(c, b, c1, b1)) continue;
if (color[a1][b1][c1] == 0) {
dist[a1][b1][c1] = dist[a][b][c] + 1;
if (is) {
color[a1][b1][c1] = 1;
qf.push(ID(a1, b1, c1));
}
else {
color[a1][b1][c1] = 2;
qb.push(ID(a1, b1, c1));
}
}
else if (color[a1][b1][c1] + color[a][b][c] == 3) {
return dist[a][b][c] + dist[a1][b1][c1] + 1;
}
else {
continue; //如果有重复的状态则跳过,不用push.
}
}
}
}
}
}
return -1;
}
int main() {
while (~scanf("%d%d%d\n", &w, &h, &n) && n) {
freopen("input.txt", "r", stdin);
for (int i = 0; i < h; i++) fgets(data1[i], 20, stdin); //此处不能用scanf来处理输入,会TLE
int cnt = 0, x[200], y[200], id[20][20]; //从图中抽取出空间并求出初始状态和目标状态
for (int i = 0; i < h; i++)
for (int j = 0; j < w; j++) {
if (data1[i][j] != '#') {
x[cnt] = i; y[cnt] = j; id[i][j] = cnt;
if (islower(data1[i][j])) s[data1[i][j] - 'a'] = cnt; //初始状态
else if (isupper(data1[i][j])) t[data1[i][j] - 'A'] = cnt; //目标状态
cnt++; //注意这里的cnt++不能偷懒在上面一行末尾,因为这样有时候cnt++会没有执行
}
}
for (int i = 0; i < cnt; i++) { //利用空格建立图
deg[i] = 0;
for (int j = 0; j < 5; j++) {
int nx = x[i] + dx[j]; int ny = y[i] + dy[j];
if (data1[nx][ny] != '#') g[i][deg[i]++] = id[nx][ny];
}
}
if (n <= 2) { deg[cnt] = 1; g[cnt][0] = cnt; s[2] = t[2] = cnt++; } //如果少于3个小鬼,就当作少的那些小鬼一开始就重合了,就全部当作一共3个小鬼算
if (n <= 1) { deg[cnt] = 1; g[cnt][0] = cnt; s[1] = t[1] = cnt++; } //不然的话就会有s[2],s[1],这种状态,要统一起来,就只有s[3]这三种。
memset(dist, 0, sizeof(dist));
memset(color, 0, sizeof(color));
if (s[0] == t[0] && s[1] == t[1] && s[2] == t[2]) printf("0\n");
else printf("%d\n", bfs());
}
return 0;
}