UVA1601-双向广度优先搜索

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <queue>
  4 #include <cstring>
  5 using namespace std;
  6 
  7 int w, h, n, s[3], t[3];
  8 char dataset[20][20];
  9 int G[200][5], vis[200][200][200], dist[200][200][200];
 10 int deg[200]; 
 11 int dx[] = {0, -1, 1, 0, 0};//这里包含了不移动的情形
 12 int dy[] = {0, 0, 0, -1, 1};
 13 
 14 inline int ID(int a, int b, int c)
 15 {
 16     return (a << 16) | (b << 8) | c;
 17 }//一种看起来屌炸天的数据整合方式
 18 
 19 inline bool conflict(int a, int b, int a2, int b2)
 20 {
 21     return ((a2 == b2) || (a == b2 && b == a2));
 22     //前者是两者运动到同一位置,后者是两者同时交换位置
 23 }
 24 
 25 int bfs()
 26 {
 27     queue<int> q;
 28     q.push(ID(s[0], s[1], s[2]));
 29     dist[s[0]][s[1]][s[2]] = 0;//dist初始值是-1,这里的0是做标记
 30     while(!q.empty())
 31     {
 32         int u = q.front();  q.pop();
 33         int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u & 0xff;
 34         //一种看起来屌炸天的数据整合方式,记住!!
 35         if(a == t[0] && b == t[1] && c == t[2]) return dist[a][b][c];
 36         //a对应了main中的cnt编号,它并没有直接验证横竖坐标而是验证了其编号
 37         for(int i = 0; i < deg[a]; i++)
 38         {//deg[a]是cnt编号为a的字母在该点的所有可行的运动方向的个数,遍历所有可行方向
 39             int a2 = G[a][i];
 40         //cnt编号为a的字母运动结果a2
 41             for(int j = 0; j < deg[b]; j++)
 42             {
 43                 int b2 = G[b][j];
 44         //cnt编号为b的字母运动结果b2
 45                 if(conflict(a, b, a2, b2))continue;
 46                 //对于产生的运动冲突,由第二个字母做出变换方向的让步
 47                 for(int k = 0; k < deg[c]; k++)
 48                 {
 49                     int c2 = G[c][k];
 50         //cnt编号为c的字母运动结果c2
 51                     if(conflict(a, c, a2, c2) || conflict(b, c, b2, c2))continue;
 52                     if(dist[a2][b2][c2] == -1)
 53                     {
 54                         dist[a2][b2][c2] = dist[a][b][c] + 1;
 55                         //这是记录步数
 56                         q.push(ID(a2, b2, c2));
 57                     }
 58                 }
 59             }
 60         }
 61     }
 62     return -1;
 63 }
 64 
 65 int main() {
 66     //freopen("input.txt", "r", stdin);
 67     while(~scanf("%d%d%d\n", &w, &h, &n) && n)
 68     {
 69         for(int i = 0; i < h; i++)  fgets(dataset[i], 20, stdin);
 70         //输入图
 71         int cnt = 0, x[200], y[200], id[20][20];
 72         for(int i = 0; i < h; i++)
 73         for(int j = 0; j < w; j++)
 74         {
 75             if(dataset[i][j] != '#')//对于所有的可行动的位置都由一个cnt编号
 76             {
 77                 //s->cnt->x与y 这是一种值得借鉴的对应关系
 78                 x[cnt] = i; y[cnt] = j; id[i][j] = cnt;//在图中赋予可行动位置唯一的id
 79                 if(islower(dataset[i][j]))  s[dataset[i][j] - 'a'] = cnt;
 80                 else if(isupper(dataset[i][j])) t[dataset[i][j] - 'A'] = cnt;
 81                 cnt++;//所有的坐标均被分配到x与y数组中,cnt是唯一的确认,将两个数据映射成了一个数据!!!
 82             }//记录bfs路径的起点与终点的信息 s数组是起点,t数组是终点,
 83         }
 84         for(int i = 0; i < cnt; i++)//对应每一个位置
 85         {
 86             deg[i] = 0;
 87             for(int j = 0; j < 5; j++)//对应不同的方向且考虑了不移动的情形
 88             {
 89                 int nx = x[i] + dx[j];  int ny = y[i] + dy[j];
 90                 if(dataset[nx][ny] != '#')  G[i][deg[i]++] = id[nx][ny];
 91                 //G[cnt编号][方向]
 92             }
 93         }
 94         //对于所有的位置的每一个可行的方向都记录下来
 95         if(n <= 2)  { deg[cnt] = 1; G[cnt][0] = cnt;    s[2] = t[2] = cnt++; }
 96         if(n <= 1)  { deg[cnt] = 1; G[cnt][0] = cnt;    s[1] = t[1] = cnt++; }//这里似乎是控制移动对象个数
 97         memset(dist, -1, sizeof(dist));
 98         printf("%d\n", bfs());
 99     }
100     return 0;
101 }

这不是我的代码,这是一个很好的代码,下面有几个方面值得借鉴学习:

1、数据结构设计十分巧妙,仅仅使用普通的数组建立的结构使得数据的查找与变换十分快捷方便

2、利用这个代码中的映射结构,提前准备好每个点可行的方向,这个可以用于平时的bfs或dfs中

猜你喜欢

转载自www.cnblogs.com/savennist/p/12238988.html