搜索_BFS

Flood_Fill

  • 从当前点开始,进行广度优先遍历,遍历过的点标记一下,模拟被洪水灌溉

1097. 池塘计数

  • 计算二维地图中的连通块的个数
#include <iostream>
#include <cstring>

using namespace std;
const int N = 1010;
typedef pair<int, int> PII;
char g[N][N];
int n, m;
bool st[N][N];
PII q[N * N];
int dx[] = {0, 1, 1, 1, 0, -1, -1, -1}, dy[] = {1, 1, 0, -1, -1, -1, 0, 1};

void bfs(int a, int b) {
    int tt = -1, hh = 0;
    q[++tt] = {a, b};
    st[a][b] = true;
    while(hh <= tt) {
        PII t = q[hh++];
        int sx = t.first, sy = t.second;
        for(int i = 0; i < 8; i++) {
            int xx = sx + dx[i], yy = sy + dy[i];
            if(xx < 0 || xx >= n || yy < 0 || yy >= m) continue;
            if(st[xx][yy]) continue;
            if(g[xx][yy] == '.') continue;
            // 标记被覆盖的
            st[xx][yy] = true;
            q[++tt] = {xx, yy};
        }
    }
}

int main() {
    cin >> n >> m;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++) 
            cin >> g[i][j];
            
    int cnt = 0;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++) {
            if(!st[i][j] && g[i][j] == 'W') {
                cnt++;
                bfs(i, j);
            }
        }
            
    cout << cnt << endl;
    return 0;
}

1098. 城堡问题

  • 统计连通块的数量、连通块中最大size
#include <iostream>
#include <cstring>
#include <queue>

using namespace std;
typedef pair<int, int> PII;
const int N = 55;
int dx[] = {0, -1, 0, 1}, dy[] = {-1, 0, 1, 0};
int g[N][N], n, m;
bool st[N][N];
queue<PII> q;

int bfs(int a, int b) {
    int area = 0;
    q.push({a, b});
    st[a][b] = true;
    while(q.size()) {
        PII t = q.front(); q.pop();
        area ++;
        int sx = t.first, sy = t.second;
        // cout << sx << " " << sy << endl;
        for(int i = 0; i < 4; i++) {
            int xx = sx + dx[i], yy = sy + dy[i];
            if(xx < 0 || xx >= n || yy < 0 || yy >= m) continue;
            if(st[xx][yy]) continue;
            if(g[sx][sy] >> i & 1) continue;
            st[xx][yy] = true;
            q.push({xx, yy});
        }
    }
    return area;
}

int main() {
    cin >> n >> m;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++)
            cin >> g[i][j];
            
    int ans = 0, cnt = 0;
    for(int i = 0; i < n; i++)  
        for(int j = 0; j < m; j++)
            if(!st[i][j]) {
                cnt++;
                ans = max(ans, bfs(i, j));
            }
    // 联通块的数量
    cout << cnt << endl;
    // 连通块的最大size
    cout << ans << endl;
    return 0;
}

1106. 山峰和山谷

  • 计数每个点周围的情况

最短路模型

迷宫问题

  • BFS搜索中记录路径
#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>

using namespace std;
typedef pair<int, int> PII;
const int N = 1010, M = N * N;
int g[N][N];
int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};
int n, m;
PII q[M];
int tt = -1, hh = 0;
PII pre[N][N];

void bfs() {
    memset(pre, -1, sizeof pre);
    // 反向搜索
    q[++tt] = {n - 1, n - 1};
    pre[n - 1][n - 1] = {0, 0};
    while(hh <= tt) {
        PII p = q[hh++];
        int px = p.first, py = p.second;
        for(int i = 0; i < 4; i++) {
            int x = px + dx[i], y = py + dy[i];
            if(x < 0 || x >= n || y < 0 || y >= n) continue;
            if(g[x][y]) continue;
            if(pre[x][y].first != -1) continue;
            q[++tt] = {x, y};
            pre[x][y] = p;
        }
    }
    
}

int main() {
    scanf("%d", &n);
    m = n;
    for(int i = 0; i < n; i++) 
        for(int j = 0; j < m; j++)
            scanf("%d", &g[i][j]);
            
    bfs();
    
    PII p(0, 0);
    
    while(true) {
        cout << p.first << " " << p.second << endl;
        if(p.first == n - 1 && p.second == n - 1) break;
        p = pre[p.first][p.second];
    }
    
    return 0;
}

188. 武士风度的牛

  • BFS求最短路
#include <iostream>
#include <cstring>

using namespace std;
typedef pair<int, int> PII;
const int N = 160, M = N * N;
char g[N][N];
int n, m, dist[N][N];
PII st, q[M];
int dx[] = {-1, -2, -2, -1, 1, 2, 2, 1}, dy[] = {2, 1, -1, -2, -2, -1, 1, 2};

int bfs() {
    memset(dist, -1, sizeof dist);
    int sx = st.first, sy = st.second;
    int tt = -1, hh = 0;
    q[++tt] = {sx, sy};
    dist[sx][sy] = 0;
    while(hh <= tt) {
        PII t = q[hh++];
        int tx = t.first, ty = t.second;
        for(int i = 0; i < 8; i++) {
            int xx = tx + dx[i], yy = ty + dy[i];
            if(xx < 0 || xx >= n || yy < 0 || yy >= m) continue;
            if(g[xx][yy] == '*') continue;
            if(dist[xx][yy] != -1) continue;
            if(g[xx][yy] == 'H') return dist[tx][ty] + 1;
            q[++tt] = {xx, yy};
            dist[xx][yy] = dist[tx][ty] + 1;
        }
    }
    return -1;
}

int main() {
    scanf("%d%d", &m, &n);
    
    
    for(int i = 0; i < n; i++) cin >> g[i];
    
    for(int i = 0; i < n; i++) 
        for(int j = 0; j < m; j++)  
            if(g[i][j] == 'K')
                st = {i, j};
    
    cout << bfs() << endl;
    
    return 0;
}

多源最短路

173. 矩阵距离

  • 每个点只会入队一次
  • 所有出队的点一定保证是距离最小的点
  • 可以用图中的1来更新其他点,将所有为1的点入队
#include <iostream>
#include <cstring>

using namespace std;
typedef pair<int, int>PII;
const int N = 1010, M = N * N;
char g[N][N];
int d[N][N];
PII q[M];
int n, m;
int dx[] = {0, 1, -1, 0}, dy[] = {1, 0, 0, -1};

void bfs() {
    memset(d, -1, sizeof d);

    int hh = 0, tt = -1;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++)
            if(g[i][j] == '1') {
                q[++tt] = {i, j};
                d[i][j] = 0;
            }
    
    while(hh <= tt) {
        PII t = q[hh++];
        int tx = t.first, ty = t.second;
        for(int i = 0; i < 4; i++) {
            int xx = tx + dx[i], yy = ty + dy[i];
            if(xx < 0 || xx >= n || yy < 0 || yy >= m) continue;
            if(d[xx][yy] != -1) continue;
            d[xx][yy] = d[tx][ty] + 1;
            q[++tt] = {xx, yy};
        }
    }
}

int main() {
    cin >> n >> m;
    
    for(int i = 0; i < n; i++)
        scanf("%s", g[i]);
        
    bfs();
    
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < m; j++) 
            printf("%d ", d[i][j]);
        printf("
");
    }
        
    return 0;
}

最小步数模型

1107. 魔板

双向广搜

175. 电路维修

  • 边权值有0、1两种,可采用双端队列广搜
    • 对于边权为0的点,说明不需要额外的花费即可到达该点,考虑到采用BFS搜索,当前正在出队的就是最小值,应该把该点放在队头,以保证BFS的两段性
    • 对于边权为1的点,说明要到达该点,需要额外的花费,应将该点放入队尾
    • 每次从队头出队元素即可

A*

  • 算法流程:
    • 采用优先队列(小根堆)存储从起点到当前点的真实距离、从当前点到终点的估计距离
    • 启发函数:从当前点到终点的估计距离
    • 每次取出优先队列的队头,遍历当前点的所有临边,将临边入队
    • 当终点第一次出队的时候结束循环

179. 八数码

  • 优先队列存储从起始状态到当前状态的距离加上当前状态到终止状态之间的距离
#include <iostream>
#include <queue>
#include <string>
#include <algorithm>
#include <unordered_map>

using namespace std;
typedef pair<int, string> PIS;

string ed = "12345678x";
unordered_map<string, pair<string, char>> pre;
unordered_map<string, int> dist;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
char op[4] = {'u', 'r', 'd', 'l'};


int fn(string s) {
    int res = 0;
    for(int i = 0; i < 9; i++)
        if(s[i] != 'x') {
            int pos = s[i] - '1';
            res += abs(i / 3 - pos / 3) + abs((i % 3) - pos % 3);
        }
    return res;
}

string bfs(string start) {
    priority_queue<PIS, vector<PIS>, greater<PIS>> hp;
    dist[start] = 0;
    hp.push({fn(start), start});
    while(!hp.empty()) {
        PIS t = hp.top(); hp.pop();
        string state = t.second;
        if(state == ed) break;
        int step = dist[state];
        int x, y;
        for (int i = 0; i < state.size(); i ++ )
            if (state[i] == 'x')
            {
                x = i / 3, y = i % 3;
                break;
            }
            
        string src = state;
        for(int i = 0; i < 4; i++) {
            int a = x + dx[i], b = y + dy[i];
            
            if(a < 0 || a >= 3 || b < 0 || b >= 3) continue;
            
            swap(state[x * 3 + y], state[a * 3 + b]);
            
            if(!dist.count(state) || dist[state] > step + 1) {
                dist[state] = step + 1;
                pre[state] = {src, op[i]};
                hp.push({dist[state] + fn(state), state});
            }
            
            swap(state[x * 3 + y], state[a * 3 + b]);
        }
    }
    string order;
    while(ed != start) {
        order += pre[ed].second;
        ed = pre[ed].first;
    }
    reverse(order.begin(), order.end());
    return order;
}

int main() {
    
    string start;
    char c;
    while(cin >> c) start += c;
    
    int eo = 0;
    for(int i = 0; i < 9; i++) 
        for(int j = i + 1; j < 9; j++)
            if(start[i] != 'x')
                if(start[i] > start[j]) 
                    eo++;
    
    if(eo & 1) puts("unsolvable");
    else cout << bfs(start) << endl;
    
    return 0;
}

178. 第K短路

  • 由于搜索空间太大,采用Astar求解
  • 使用当前点到终点的最短距离作为启发函数
#include <iostream>
#include <cstring>
#include <queue>

using namespace std;
typedef pair<int, int> PII;
typedef pair<int, PII> PIII;
const int N = 1010, M = N * N;
int e[M], w[M], ne[M], h[N], rh[N], idx;
bool st[N];
int dist[N];
int S, T, K;

void add(int h[], int a, int b, int c) {
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}

void Dijkstra() {
    memset(dist, 0x3f, sizeof dist);
    priority_queue<PII, vector<PII>, greater<PII>> hp;
    hp.push({0, T});
    dist[T] = 0;
    while(hp.size()) {
        PII t = hp.top(); hp.pop();
        int ver = t.second, distance = t.first;
        if(st[ver]) continue;
        st[ver] = true;
        for(int i = rh[ver]; ~i; i = ne[i]) {
            int j = e[i];
            if(dist[j] > dist[ver] + w[i]) {
                dist[j] = dist[ver] + w[i];
                hp.push({dist[j], j});
            }
        }
    }
}

int Astar() {
    if(dist[S] == 0x3f3f3f3f) return -1;
    
    int cnt = 0;
    priority_queue<PIII, vector<PIII>, greater<PIII>> ahp;
    ahp.push({dist[S], {0, S}});
    while(ahp.size()) {
        PIII t = ahp.top(); ahp.pop();
        int ver = t.second.second;
        int distance = t.second.first;
        if(ver == T) cnt++;
        if(cnt == K) return distance;
        
        for(int i = h[ver]; ~i; i = ne[i]) {
            int j = e[i];
            ahp.push({distance + w[i] + dist[j], {distance + w[i], j}});
        }
    }
    return -1;
}

int main() {
    int n, m; cin >> n >> m;
    memset(h, -1, sizeof h);
    memset(rh, -1, sizeof rh);
    for(int i = 0; i < m; i++) {
        int a, b, c; scanf("%d%d%d", &a, &b, &c);
        add(h, a, b, c);
        add(rh, b, a, c);
    }
    scanf("%d%d%d", &S, &T, &K);
    if(S == T) K++;
    Dijkstra();
    printf("%d
", Astar());
    return 0;
}
原文地址:https://www.cnblogs.com/Hot-machine/p/13418857.html