关于BFS

    Emmmm ,本着不再发明轮子的原则,网上BFS教程一搜一大堆,所以原理就不再讲了,给出几道题加深理解好了>_<

(不喜欢看博客的看书也行)

1,

  • 给你一个n*n的0/1矩阵
  • 问完全被1包围的0有多少个(包围的定义类似于围棋)
  • n <= 1000 ;

(请先自己思考再往下翻)

  SOL :

  其实思路很简单,只要从边界处的0开始广搜,能搜到的都变为1,最后剩下的零就是被1完全包围的

 

2,

 

  • 给你一个n*n的0/1矩阵
  • 不同位置之间的移动规则是相邻(上下左右)且亦或值为1(就是一个是1一个是0)。
  • 给你m个询问,每个询问为一个二元组(x,y),问从点(x,y)最多可以到达几个点(包括自身)。
  • n ≤ 1000, m ≤ 100000 ;

(请先自己思考)

  SOL :

   首先这明显是广搜题,但如果我们每个询问都去广搜一遍的话复杂度最坏是O(n^2*m)的,显然不行对吧。。

   其实我们可以预处理联通块,然后把联通块内的坐标的答案值记录为所属联通块大小,然后按询问回答即可

   这样的话预处理是O(n^2),询问是O(m),总复杂度就是O(n^2+m)

 

3,


  这道题题目较复杂,我发个网址:https://www.luogu.org/problemnew/show/P1126

 

   SOL:

   首先,由于机器人走的格子与障碍物防止格子并不相同,所以我们要把障碍物从原坐标转化为机器人所走格子的坐标

   然后我们考虑这道题,因为与方向有关,所以我们在广搜时要多加一个参数——方向,也就是说队列中记录的是一个状态(包括横纵坐标和方向)。

   接着层层向外扩展,直到状态横纵坐标与终点横纵坐标相等即可

 这道题较为经典,附上代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N = 50 + 2 ;

const int dx[]={-1,-1,0,0} ;
const int dy[]={-1,0,-1,0} ;

const int cx[]={0,-1,0,1} ;
const int cy[]={-1,0,1,0} ;

inline int read()
{
    int k = 0 , f = 1 ; char c = getchar() ;
    for( ; !isdigit(c) ; c = getchar())
      if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar())
      k = k*10 + c-'0' ;
    return k*f ;
}

struct node {
    int x,y,d ; int ti ;
}a,b,c ;
queue<node>q ;

int n,m ; int sx,sy,ex,ey,sd ; char dd ;
bool gg[N][N], hh[N][N] ;
bool vis[11000] ;

inline int fun(node s) {  // 把每种状态转化成一个独一无二的数,记录该状态有没有被访问过 
    return s.d*2600 + s.x*50 + s.y ;
}

int main()
{
    n = read() ; m = read() ;
    int xx, yy ;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) {
            gg[i][j] = read() ;
            if(gg[i][j])
             for(int k=0;k<4;k++) {  // 把障碍物影响到的点映射到机器人所走坐标 
                xx = i + dx[k] ; yy = j + dy[k] ;
                if(xx < 1 || yy < 1 || xx >= n || yy >= m) continue ; // 因为靠墙的位置走不了,所以机器人所走矩形为(n-1)*(m-1) 
                hh[xx][yy] = 1 ;
             }
        }
    sx = read() ; sy = read() ; ex = read() ; ey = read() ;
    if(hh[sx][sy] || hh[ex][ey]) {
        printf("-1") ; return 0 ;
    }
    cin>>dd ;
    switch(dd) { // 0-3分别表示 左,上,右,下 
        case 'W' : sd = 0 ;
          break ;
        case 'N' : sd = 1 ;
          break ;
        case 'E' : sd = 2 ;
          break ;
        case 'S' : sd = 3 ;
          break ;
    }
    a.x = sx ; a.y = sy ; a.d = sd ; a.ti = 0 ;
    q.push(a) ; vis[fun(a)] = 1 ;
    while(!q.empty()) {
        a = q.front() ; q.pop() ;
        if(a.x == ex && a.y == ey) {
            printf("%d",a.ti) ;
            return 0 ;
        }
        a.ti ++ ;
        a.d = (a.d + 1) % 4 ;
        if(!vis[fun(a)]){
            q.push(a) ; vis[fun(a)] = 1 ;
        }
        a.d = (a.d + 2) % 4 ; // 减2加2模4意义下相同,为防止出现负数就写成+2 
        if(!vis[fun(a)]) {
            q.push(a) ; vis[fun(a)] = 1 ;
        } 
        a.d = (a.d + 1) % 4 ; // 回到原来方向 
        for(int i=1;i<=3;i++) {
            a.x += cx[a.d] ; a.y += cy[a.d] ;
            if(a.x < 1 || a.y < 1 || a.x >= n || a.y >= m) break ;
            if(hh[a.x][a.y]) break ;
            if(vis[fun(a)]) continue ;
            q.push(a) ; vis[fun(a)] = 1 ;
        }
    }
    printf("-1") ;
    return 0 ;
}

我的坐标转化方式:

(暂完,以后遇到题可能还会更) 

posted @ 2018-02-11 21:34  zubizakeli  阅读(161)  评论(0编辑  收藏  举报