acwing算法提高之搜索--BFS中的Flood Fill和最短路模型

123 篇文章 1 订阅

1 专题说明

本专题用来记录使用Flood Fill算法和最短路模型解决的搜索问题。

2 训练

题目11097池塘计数

C++代码如下,
方法1:使用bfs算法

#include <iostream>
#include <queue>

using namespace std;

const int N = 1010;

int n, m;
char g[N][N];
bool st[N][N];

void bfs(int i, int j) {
    queue<pair<int,int>> q;
    q.push(make_pair(i, j));
    st[i][j] = true;
    
    while (!q.empty()) {
        pair<int,int> t = q.front();
        q.pop();
        
        int x = t.first, y = t.second;
        
        for (int dx = -1; dx <= 1; ++dx) {
            for (int dy = -1; dy <= 1; ++dy) {
                if (dx == 0 && dy == 0) continue;
                
                int nx = x + dx, ny = y + dy;
                if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
                if (st[nx][ny]) continue;
                if (g[nx][ny] != 'W') continue;
                
                q.push(make_pair(nx, ny));
                st[nx][ny] = true;
            }
        }        
    }
    
    return;
}

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

方法2:使用并查集

#include <iostream>
#include <vector>

using namespace std;

const int N = 1010;
const int M = 1e6 + 10;

int n, m;
char g[N][N];

int p[M];
int cnt;

int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

void merge(int a, int b) {
    int pa = find(a);
    int pb = find(b);
    if (pa == pb) return;
    
    p[pa] = pb;
    cnt--;
    return;
}

int main() {
    
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            cin >> g[i][j];
            if (g[i][j] == 'W') cnt++;
        }
    }
    
    //并查集初始化
    for (int k = 0; k < n * m; ++k) {
        p[k] = k;
    }
    
    int dirs[8][2] = {{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1}};
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            if (g[i][j] == 'W') {
                for (int k = 0; k < 8; ++k) {
                    int x = i + dirs[k][0];
                    int y = j + dirs[k][1];
                    if (x < 0 || x >= n || y < 0 || y >= m) continue;
                    if (g[x][y] != 'W') continue;
                    
                    int a = i * m + j; //乘以列数
                    int b = x * m + y;
                    merge(a, b);
                }
            }
        }
    }
    
    cout << cnt << endl;
    
    return 0;
}

题目21098城堡问题

C++代码如下,
方法1:bfs

#include <iostream>
#include <queue>

using namespace std;

const int N = 60;

int n, m;
int g[N][N];
bool st[N][N];

int cnt = 0; //房间总数
int maxv = 0; //房间最大面积

void bfs(int i, int j) {
    int dirs[4][2] = {{0,-1}, {-1,0}, {0,1}, {1,0}};
    //1表示西墙,2表示北墙,4表示东墙,8表示南墙
    
    queue<pair<int,int>> q;
    q.push(make_pair(i,j));
    st[i][j] = true;
    
    int curv = 0; //当前房间面积
    while (!q.empty()) {
        pair<int,int> t = q.front();
        q.pop();
        
        curv ++;
        
        int x = t.first, y = t.second;
        for (int k = 0; k < 4; ++k) {
            int nx = x + dirs[k][0], ny = y + dirs[k][1];
            if ((g[x][y] >> k) & 1) continue; //有一堵墙
            if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
            if (st[nx][ny]) continue;
            
            q.push(make_pair(nx,ny));
            st[nx][ny] = true;
        }
    }
    maxv = max(maxv, curv);
    return;
}

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

方法2:并查集

#include <iostream>

using namespace std;

const int N = 60, M = 2500 + 10;
int n, m;
int g[N][N];
int p[M];
int cnt[M]; //每个并查集中的元素数目,只对根结点有效
int tot; //房间总数
int maxv; //房间最大面积

int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

void merge(int a, int b) {
    int pa = find(a), pb = find(b);
    if (pa == pb) return;
    p[pa] = pb;
    cnt[pb] += cnt[pa];
    tot--;
    maxv = max(maxv, cnt[pb]);
    return;
}


int main() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            cin >> g[i][j];
        }
    }
    
    //初始化并查集
    for (int k = 0; k < n * m; ++k) {
        p[k] = k;
        cnt[k] = 1;
    }
    tot = n * m;
    maxv = 1;
    
    int dirs[4][2] = {{0,-1}, {-1,0}, {0,1}, {1,0}};
    //1表示西墙,2表示北墙,4表示东墙,8表示南墙
    
    //遍历并查集
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            for (int k = 0; k < 4; ++k) {
                int a = i * m + j;
                
                int x = i + dirs[k][0], y = j + dirs[k][1];
                if (x < 0 || x >= n || y < 0 || y >= m) continue;
                if ((g[i][j] >> k) & 1) continue; //有一堵墙,跳过
                
                int b = x * m + y;
                merge(a, b);
            }
        }
    }
    
    cout << tot << endl << maxv << endl;
    
    return 0;
}

题目31106山峰和山谷

C++代码如下,

#include <iostream>
#include <queue>

using namespace std;

const int N = 1010;
int n;
int g[N][N];
bool st[N][N];

int cnt_crest = 0; //峰顶数目
int cnt_valley = 0; //谷底数目

void bfs(int i, int j) {
    
    queue<pair<int,int>> q;
    q.push(make_pair(i,j));
    st[i][j] = true;
    
    bool tag1 = false; //出现其它元素大于集合内元素
    bool tag2 = false; //出现其它元素小于集合内元素
    while (!q.empty()) {
        pair<int,int> t = q.front();
        q.pop();
        
        int x = t.first; 
        int y = t.second;
        
        for (int dx = -1; dx <= 1; ++dx) {
            for (int dy = -1; dy <= 1; ++dy) {
                if (dx == 0 && dy == 0) continue;
                
                int nx = x + dx;
                int ny = y + dy;
            
                if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue;
                
                if (g[nx][ny] > g[i][j]) {
                    tag1 = true;
                } else if (g[nx][ny] < g[i][j]) {
                    tag2 = true;
                }
                
                if (st[nx][ny]) continue;
                
                if (g[nx][ny] == g[i][j]) {
                    q.push(make_pair(nx, ny));
                    st[nx][ny] = true;
                } 
                
            }
        }
    }
    
    if (tag1 == false && tag2 == false) {
        cnt_crest++;
        cnt_valley++;
    } else if (tag1 == true && tag2 == false) {
        cnt_valley++;
    } else if (tag1 == false && tag2 == true) {
        cnt_crest++;
    }
    
    return;
}

int main() {
    cin >> n;
    
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            cin >> g[i][j];
        }
    }
    
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (!st[i][j]) {
                bfs(i, j);
            }
        }
    }
     
    cout << cnt_crest << " " << cnt_valley << endl;
       
    return 0;
}

题目41076迷宫问题

C++代码如下,

#include <iostream>
#include <queue>
#include <unordered_map> 
#include <vector>
#include <algorithm>

using namespace std;

const int N = 1010;
int n;
int g[N][N];
bool st[N][N];

unordered_map<int, int> map_node_prevnode;

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            cin >> g[i][j];
        }
    }
    
    queue<pair<int,int>> q;
    q.push(make_pair(0,0));
    st[0][0] = true;
    
    int dirs[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}};
    
    while (!q.empty()) {
        pair<int, int> t = q.front();
        q.pop();
        
        int x = t.first;
        int y = t.second;
        
        for (int k = 0; k < 4; ++k) {
            int nx = x + dirs[k][0];
            int ny = y + dirs[k][1];
            
            if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue;
            if (st[nx][ny]) continue;
            if (g[nx][ny] != 0) continue;
            
            //可以走到(nx,ny)
            int a = x * n + y;
            int b = nx * n + ny;
            map_node_prevnode[b] = a; 
            
            q.push(make_pair(nx,ny));
            st[nx][ny] = true;
            
            if (nx == n - 1 && ny == n - 1) break; //退出while循环
        }
    }
    
    //输出路径
    vector<pair<int,int>> paths = {make_pair(n-1, n-1)};
    int node = n * n - 1;
    while (node != 0) {
        int prevnode = map_node_prevnode[node];
        int prevx = prevnode / n;
        int prevy = prevnode % n;
        paths.emplace_back(prevx, prevy);
        node = prevnode; //更新node
    }
    
    reverse(paths.begin(), paths.end());
    
    for (auto [x, y] : paths) {
        cout << x << " " << y << endl;
    }
    
    return 0;
}

题目5188武士风度的牛

C++代码如下,

#include <iostream>
#include <queue>

using namespace std;

const int N = 160;
char g[N][N];
bool st[N][N];
int n, m;

pair<int, int> snode, enode;

int main() {
    cin >> m >> n;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            cin >> g[i][j];
            if (g[i][j] == 'K') snode = make_pair(i, j);
            else if (g[i][j] == 'H') enode = make_pair(i, j);
        }
    }
    
    queue<pair<int,int>> q;
    q.push(snode);
    st[snode.first][snode.second] = true;
    
    int res = 0;
    int dirs[8][2] = {{1,2}, {2,1}, {-1,2}, {-2,1}, {-1,-2}, {-2,-1}, {1,-2}, {2,-1}};
    
    while (!q.empty()) {
        res++;
        
        int curn = q.size();
        while (curn--) {
            pair<int,int> t = q.front();
            q.pop();
            
            int x = t.first;
            int y = t.second;
            for (int k = 0; k < 8; ++k) {
                int nx = x + dirs[k][0];
                int ny = y + dirs[k][1];
                
                if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
                if (st[nx][ny]) continue;
                if (g[nx][ny] == '*') continue;
                
                q.push(make_pair(nx,ny));
                st[nx][ny] = true;
                
                if (make_pair(nx, ny) == enode) {
                    cout << res << endl;
                    return 0;
                }
            }
        }
        
    }
    
    return 0;
}

题目61100抓住那头牛

C++代码如下,
方法1:bfs

#include <iostream>
#include <queue>

using namespace std;

const int N = 100010;
int a, b;
bool st[N];

int main() {
    cin >> a >> b;
    
    int res = 0;
    
    queue<int> q;
    q.push(a);
    st[a] = true;
    
    while (!q.empty()) {
        
        
        int curn = q.size();
        
        while (curn--) {
            auto t = q.front();
            q.pop();
            
            if (t == b) { //在弹出时判断是否相等
                cout << res << endl;
                return 0;
            }
            
            //t可以走到哪里
            if (t + 1 < N && t + 1 >= 0 && !st[t+1]) {
                q.push(t + 1);
                st[t+1] = true;
            } 
            
            if (t - 1 < N && t - 1 >= 0 && !st[t-1]) {
                q.push(t - 1);
                st[t-1] = true;
            }
            
            if (2 * t < N && 2 * t >= 0 && !st[2 * t]) {
                q.push(2 * t);
                st[2 * t] = true;
            }
        }
        res++;
    }
    
    return 0;
}

方法2:动态规划

#include <iostream>
#include <cstring>

using namespace std;

const int N = 100010;
int a, b;
int f[N]; //f[i]:从a到i的最小步数

int main() {
    cin >> a >> b;
    
    //初始化
    memset(f, 0x3f, sizeof f);
    f[a] = 0;
    
    //<=a状态转移
    for (int i = a; i >= 0; --i) {
        f[i] = min(f[i], f[i+1] + 1);
    }
    
    //>a状态转移
    //(1)按照以下路径过来i-1 -> i
    //(2)i是偶数,按照以下路径过来i/2 -> i
    //(3)i是奇数,按照以下路径过来(i-1)/2 -> i-1 -> i 
    //(4)i是奇数,按照以下路径过来(i+1)/2 -> i+1 -> i
    for (int i = a + 1; i <= b; ++i) {
        f[i] = min(f[i], f[i-1] + 1); 
        if (i % 2 == 0) {
            f[i] = min(f[i], f[i/2] + 1);
        } else {
            f[i] = min(f[i], f[(i-1)/2] + 2);
            f[i] = min(f[i], f[(i+1)/2] + 2);
        }
    }
    
    cout << f[b] << endl;
    
    return 0;
}
  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YMWM_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值