uva 1601万圣节后的早晨 双向bfs

第一次接触双向bfs,认真学习了别人的代码,跟大佬学习一波,双向dfs并不是节约一半的时间,他的时间当n约大的时候,节约的时间越多,所以当n很小的时候,还是用dfs比较简单一点。

双向的bfs就是在一个在起点一个在终点,分别搜索点,分别对走过的位置做标记,如果走过的位置有相同的,则说明是相通的

从这一次的学习中,更加对bfs状态搜索有了一些了解,自己看了一个双向的,加一改进,然后自己又写了一个单向的。

下面讲一下这个题的思路,这个题,按照紫书的思路,把所有的空格拿出来,再建立一个图,然后,需要注意的是小鬼所走过的地方不能有重复,还有这个题的数据并不是很大

200 * 200 * 200;直接三维数组表示状态,记录一下位数,这个题,在用双向的时候,有一些细节需要注意

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       

using namespace std;
int w, h, n, s[3], t[3];//s代表起点,t代表终点
char dataset[20][20];
int G[200][5], color[200][200][200], dist[200][200][200], redist[200][200][200];
int deg[200];   //记录每个编号为i的空格周围可以走的步数

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;
}

inline bool conflict(int a, int b, int a2, int b2)
{
    return ((a2 == b2) || (a == b2 && b == a2));
}

int bfs()
{
    queue
       
       
         qf; //记录正向bfs queue 
        
          qb; //记录反向bfs dist[s[0]][s[1]][s[2]] = 0; dist[t[0]][t[1]][t[2]] = 1; //分别记录正反两种遍历走了多少步数 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; color[t[0]][t[1]][t[2]] = 2; //分别标注正反两种遍历已经走过的 while(!qf.empty() || !qb.empty()) { int fnum = qf.size(), bnum = qb.size(); while(fnum--) { int u = qf.front(); qf.pop(); int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u & 0xff;//oxff 代表225 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) || conflict(b, c, b2, c2)) continue; if(color[a2][b2][c2] == 0)//没走过的为零,走过的为1 { dist[a2][b2][c2] = dist[a][b][c] + 1; color[a2][b2][c2] = 1; qf.push(ID(a2, b2, c2)); } else if(color[a2][b2][c2] == 2)//判断走过的如果是2则说明相遇 { return dist[a][b][c] + dist[a2][b2][c2]; } } } } } while(bnum--) { int 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 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) || conflict(b, c, b2, c2)) continue; if(color[a2][b2][c2] == 0) { dist[a2][b2][c2] = dist[a][b][c] + 1; color[a2][b2][c2] = 2; qb.push(ID(a2, b2, c2)); } else if(color[a2][b2][c2] == 1) { return dist[a][b][c] + dist[a2][b2][c2]; } } } } } } return -1; } int main() { while(~scanf("%d%d%d\n", &w, &h, &n) && n) { for(int i = 0; i < h; i++) fgets(dataset[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(dataset[i][j] != '#') { x[cnt] = i; y[cnt] = j; id[i][j] = cnt; if(islower(dataset[i][j])) s[dataset[i][j] - 'a'] = cnt; //初始状态 else if(isupper(dataset[i][j])) t[dataset[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(dataset[nx][ny] != '#') G[i][deg[i]++] = id[nx][ny]; } } if(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++; } 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; } 
         
       
      
      
     
     
    
    
   
   
下面再附上单向的bfs  单向的搜索也很简单,直接暴力也过了,看来这个题对时间的要求并不是很高

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       

using namespace std;
int w, h, n, s[3], t[3];//s代表起点,t代表终点
char dataset[20][20];
int G[200][5], color[200][200][200], dist[200][200][200], redist[200][200][200];
int deg[200];   //记录每个编号为i的空格周围可以走的步数

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;
}

inline bool conflict(int a, int b, int a2, int b2)
{
    return ((a2 == b2) || (a == b2 && b == a2));
}

int bfs()
{
    queue
       
       
         qf; //记录正向bfs //queue 
        
          qb; //记录反向bfs memset(dist,-1,sizeof(dist)); qf.push(ID(s[0],s[1],s[2])); dist[s[0]][s[1]][s[2]] = 0; while(!qf.empty() ) { int u = qf.front(); qf.pop(); int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u & 0xff;//oxff 代表225 if(a == t[0] && b == t[1] && c == t[2]) return dist[a][b][c]; 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(dist[a2][b2][c2] != -1) continue; dist[a2][b2][c2] = dist[a][b][c] + 1; qf.push(ID(a2, b2, c2)); } } } } return -1; } int main() { //freopen("input.txt", "r", stdin); while(~scanf("%d%d%d\n", &w, &h, &n) && n) { for(int i = 0; i < h; i++) fgets(dataset[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(dataset[i][j] != '#') { x[cnt] = i; y[cnt] = j; id[i][j] = cnt; if(islower(dataset[i][j])) s[dataset[i][j] - 'a'] = cnt; //初始状态 else if(isupper(dataset[i][j])) t[dataset[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(dataset[nx][ny] != '#') G[i][deg[i]++] = id[nx][ny]; } } if(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++; } 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; } 
         
       
      
      
     
     
    
    
   
   

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值