UVA1601The Morning after Halloween 单向加双向bfs
生活随笔
收集整理的這篇文章主要介紹了
UVA1601The Morning after Halloween 单向加双向bfs
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題意:w*h(w,h16)網格上有n(n3)個小寫字母(代表鬼)。要求把它們分別移動到對應的大寫字母里。每步可以有多個鬼同時移動(均為往上下左右四個方向之一移動),但每步結束之后任何兩個鬼不能占用同一個位置,也不能在一步之內交換位置。
先來第一種單向bfs,需要注意的是如果像平常bfs判斷的話,一定會超時,因為每個節點每次有5種移動方式,3個節點,最多有種情況,但是可以將空地拿出來,變成圖。題目中有一個非常好用的條件——任何一個2*2子網格中至少有一個障礙格,最多16*16個格子,每四個有一個障礙物,也就是256*(3/4)=192,所以空地有最多192個,判重的話就用200*200*200=8000000數組來存,另外圖是將二維坐標映射成一維數組。開始時候每次找到一個空地那么空地數量加一,也就是坐標映射值。一開始竟然沒有想到。。
#include <cstdio> #include <cstring> #include <cctype> #include <list> #include <algorithm>using namespace std;int w, h, n;char pic[20][20]; // 輸入 int num[20][20]; // 輸入中的位置→圖中節點的編號 int vis[200][200][200]; // 標記數組 int connect[200][200]; // 鄰接表 int all; // 圖中節點的數量int que[10000000][4]; // BFS隊列 int goal[4]; // 目標狀態inline void BFS() {// 初始化memset(vis, 0, sizeof(vis));int fro = 0, rear = 1;vis[que[0][1]][que[0][2]][que[0][3]] = true;while(fro < rear) {int &step = que[fro][0], &a = que[fro][1], &b = que[fro][2], &c = que[fro][3];if(a == goal[1] && b == goal[2] && c == goal[3]) { goal[0] = step; return; }for(int i = 0, t1; i <= connect[a][0]; ++i) {t1 = (i == 0 ? a : connect[a][i]);for(int j = 0, t2; j <= connect[b][0]; ++j) {t2 = (j == 0 ? b : connect[b][j]);for(int k = 0, t3; k <= connect[c][0]; ++k) {t3 = (k == 0 ? c : connect[c][k]);// 判斷沖突-----if((t1 && t2 && t1 == t2) || (t1 && t3 && t1 == t3) || (t2 && t3 && t2 == t3)) continue; // 不能重合if(t1 && t2 && t1 == b && t2 == a) continue; // t1,t2不能對穿if(t1 && t3 && t1 == c && t3 == a) continue; // t1,t3不能對穿if(t2 && t3 && t2 == c && t3 == b) continue; // t2,t3不能對穿// ----------if(!vis[t1][t2][t3]) {vis[t1][t2][t3] = 1;que[rear][0] = step + 1, que[rear][1] = t1, que[rear][2] = t2, que[rear][3] = t3;++rear;}}}}++fro;} }int main() {int _t = 0;while(scanf("%d%d%d\n", &w, &h, &n) && w && h && n) {// 讀取輸入-----for(int i = 0; i != h; ++i) fgets(pic[i],20,stdin);// ----------// 根據輸入建立圖-----// 初始化memset(connect, 0, sizeof(connect));all = 0;// 獲得編號for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) {if(pic[i][j] != '#') num[i][j] = ++all;else num[i][j] = 0;}// 建立圖for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) if(num[i][j]) {int &pos = num[i][j];if(num[i + 1][j]) connect[pos][++connect[pos][0]] = num[i + 1][j];if(num[i - 1][j]) connect[pos][++connect[pos][0]] = num[i - 1][j];if(num[i][j + 1]) connect[pos][++connect[pos][0]] = num[i][j + 1];if(num[i][j - 1]) connect[pos][++connect[pos][0]] = num[i][j - 1];}// ----------// 尋找初始狀態和目標狀態(測了一下字母范圍只在abc之間所以偷懶就這么寫了)//初始化que[0][0] = que[0][1] = que[0][2] = que[0][3] = 0;goal[0] = goal[1] = goal[2] = goal[3] = 0;// 尋找初始狀態for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) if(islower(pic[i][j])) {if(pic[i][j] == 'a') que[0][1] = num[i][j];if(pic[i][j] == 'b') que[0][2] = num[i][j];if(pic[i][j] == 'c') que[0][3] = num[i][j];}// 尋找目標狀態for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) if(isupper(pic[i][j])) {if(pic[i][j] == 'A') goal[1] = num[i][j];if(pic[i][j] == 'B') goal[2] = num[i][j];if(pic[i][j] == 'C') goal[3] = num[i][j];}// ----------BFS();printf("%d\n", goal[0]);} }正常思路是這個但是超時,下面看一下作者寫的單向bfs,又是二進制。。
區別就是他使用了編碼隊列
#include<cstdio>#include<cstring>#include<cctype>#include<queue>using namespace std;const int maxs = 20;const int maxn = 150; // 75% cells plus 2 fake nodesconst int dx[]={1,-1,0,0,0}; // 4 moves, plus "no move"const int dy[]={0,0,1,-1,0};inline int ID(int a, int b, int c) {return (a<<16)|(b<<8)|c;}int s[3], t[3]; // starting/ending position of each ghostint deg[maxn], G[maxn][5]; // target cells for each move (including "no move")inline bool conflict(int a, int b, int a2, int b2) {return a2 == b2 || (a2 == b && b2 == a);}int d[maxn][maxn][maxn]; // distance from starting stateint bfs() {queue<int> q;memset(d, -1, sizeof(d));q.push(ID(s[0], s[1], s[2])); // starting noded[s[0]][s[1]][s[2]] = 0;while(!q.empty()) {int u = q.front(); q.pop();int a = (u>>16)&0xff, b = (u>>8)&0xff, c = u&0xff;if(a == t[0] && b == t[1] && c == t[2]) return d[a][b][c]; // solution foundfor(int i = 0; i < deg[a]; i++) {int a2 = G[a][i];for(int j = 0; j < deg[b]; j++) {int b2 = G[b][j];if(conflict(a, b, a2, b2)) continue;for(int k = 0; k < deg[c]; k++) {int c2 = G[c][k];if(conflict(a, c, a2, c2)) continue;if(conflict(b, c, b2, c2)) continue;if(d[a2][b2][c2] != -1) continue;d[a2][b2][c2] = d[a][b][c]+1;q.push(ID(a2, b2, c2));}}}}return -1;}int main() {int w, h, n; while(scanf("%d%d%d\n", &w, &h, &n) == 3 && n) {char maze[20][20];for(int i = 0; i < h; i++)fgets(maze[i], 20, stdin);// extract empty cellsint cnt, x[maxn], y[maxn], id[maxs][maxs]; // cnt is the number of empty cellscnt = 0;for(int i = 0; i < h; i++)for(int j = 0; j < w; j++)if(maze[i][j] != '#') {x[cnt] = i; y[cnt] = j; id[i][j] = cnt;if(islower(maze[i][j])) s[maze[i][j] - 'a'] = cnt;else if(isupper(maze[i][j])) t[maze[i][j] - 'A'] = cnt;cnt++;}// build a graph of empty cellsfor(int i = 0; i < cnt; i++) {deg[i] = 0;for(int dir = 0; dir < 5; dir++) {int nx = x[i]+dx[dir], ny = y[i]+dy[dir];// "Outermost cells of a map are walls" means we don't need to check out-of-boundif(maze[nx][ny] != '#') G[i][deg[i]++] = id[nx][ny];}}// add fakes nodes so that in each case we have 3 ghosts. this makes the code shorterif(n <= 2) { deg[cnt] = 1; G[cnt][0] = cnt; s[2] = t[2] = cnt++; }if(n <= 1) { deg[cnt] = 1; G[cnt][0] = cnt; s[1] = t[1] = cnt++; }printf("%d\n", bfs());}return 0;}雙向bfs:
就是終點和起點一起開始,看誰先到達對方經過的點。具體實現看代碼。
#include<cstdio> #include<cstring> #include<cctype> #include<queue> using namespace std; const int maxs = 20; const int maxn = 150; // 75% cells plus 2 fake nodes const int dx[] = { 1,-1,0,0,0 }; // 4 moves, plus "no move" const int dy[] = { 0,0,1,-1,0 }; inline int ID(int a, int b, int c) {return (a << 16) | (b << 8) | c; } int s[3], t[3]; // starting/ending position of each ghost int deg[maxn], G[maxn][5]; // target cells for each move (including "no move")inline bool conflict(int a, int b, int a2, int b2) {return (a2 == b2) || (a2 == b && b2 == a); }int d[maxn][maxn][maxn]; // distance from starting state int vis[maxn][maxn][maxn]; int bfs() {queue<int> q;queue<int>p;memset(d, 0, sizeof(d));memset(vis, 0, sizeof(vis));q.push(ID(s[0], s[1], s[2])); // starting nodep.push(ID(t[0], t[1],t[2]));d[s[0]][s[1]][s[2]] = 0;d[t[0]][t[1]][t[2]] = 1;vis[s[0]][s[1]][s[2]] = 1;//將從起點經過的標記為1vis[t[0]][t[1]][t[2]] = 2;//從終點開始的標記為2while (!q.empty()||!q.empty()) {int c1 = q.size(); int c2 = p.size();while (c1--) {int u = q.front(); q.pop();int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u & 0xff;for (int i = 0; i < deg[a]; i++) {int a2 = G[a][i];for (int j = 0; j < deg[b]; j++) {int b2 = G[b][j];if (conflict(a, b, a2, b2)) continue;for (int k = 0; k < deg[c]; k++) {int c2 = G[c][k];if (conflict(a, c, a2, c2)) continue;if (conflict(b, c, b2, c2)) continue;if (!vis[a2][b2][c2]) {vis[a2][b2][c2] = 1;//d[a2][b2][c2] = d[a][b][c] + 1;q.push(ID(a2, b2, c2));}else if (vis[a2][b2][c2] == 2) {//走到終點經過的節點return d[a][b][c] + d[a2][b2][c2];//返回起點走過的路程加上從終點走過的路程}}}}}while (c2--) {int u = p.front(); p.pop();int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u & 0xff;for (int i = 0; i < deg[a]; i++) {int a2 = G[a][i];for (int j = 0; j < deg[b]; j++) {int b2 = G[b][j];if (conflict(a, b, a2, b2)) continue;for (int k = 0; k < deg[c]; k++) {int c2 = G[c][k];if (conflict(a, c, a2, c2)) continue;if (conflict(b, c, b2, c2)) continue;if (!vis[a2][b2][c2]) {vis[a2][b2][c2] = 2;//d[a2][b2][c2] = d[a][b][c] + 1;p.push(ID(a2, b2, c2));}else if (vis[a2][b2][c2] == 1) {//走到起點經過的節點return d[a][b][c] + d[a2][b2][c2];//返回起點走過的路程加上從終點走過的路程}}}}}}return -1;}int main() {int w, h, n;while (scanf("%d%d%d\n", &w, &h, &n) == 3 && n) {char maze[20][20];for (int i = 0; i < h; i++)fgets(maze[i], 20, stdin);// extract empty cellsint cnt, x[maxn], y[maxn], id[maxs][maxs]; // cnt is the number of empty cellscnt = 0;for (int i = 0; i < h; i++)for (int j = 0; j < w; j++)if (maze[i][j] != '#') {x[cnt] = i; y[cnt] = j; id[i][j] = cnt;if (islower(maze[i][j])) s[maze[i][j] - 'a'] = cnt;else if (isupper(maze[i][j])) t[maze[i][j] - 'A'] = cnt;cnt++;}// build a graph of empty cellsfor (int i = 0; i < cnt; i++) {deg[i] = 0;for (int dir = 0; dir < 5; dir++) {int nx = x[i] + dx[dir], ny = y[i] + dy[dir];// "Outermost cells of a map are walls" means we don't need to check out-of-boundif (maze[nx][ny] != '#') G[i][deg[i]++] = id[nx][ny];}}// add fakes nodes so that in each case we have 3 ghosts. this makes the code shorterif (n <= 2) { deg[cnt] = 1; G[cnt][0] = cnt; s[2] = t[2] = cnt++; }if (n <= 1) { deg[cnt] = 1; G[cnt][0] = cnt; s[1] = t[1] = cnt++; }printf("%d\n", bfs());}return 0; }時間從850ms變成470ms了
總結
以上是生活随笔為你收集整理的UVA1601The Morning after Halloween 单向加双向bfs的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UVA10603 倒水问题
- 下一篇: 埃及分数问题——迭代加深搜索