算法训练三(DFS、BFS、回溯)(含解题思路)(下)

目录

7-13 桥短几何(深度优先搜索+广度优先搜索)

AC代码:

7-15 01迷宫(广度优先搜索)

AC代码:

7-17 生化危机(图的深度优先搜索)

AC代码:

7-18 模拟炸弹人(广度优先搜索)

AC代码:

7-19 N皇后问题(回溯)

AC代码:

7-20 图着色问题(图的应用)

AC代码:

7-21 排座位(图的应用)

AC代码:

7-22 红色警报 (图的连通分量)

AC代码:

7-23 列出连通集(深度优先搜索/广度优先搜索)

AC代码:

7-25 功夫传人 (广度优先搜索)

AC代码:

7-26 深入虎穴 (广度优先搜索)

AC代码:

7-27 肿瘤诊断 (三维广度优先搜索)

AC代码:


因题集题目较多,上半部分请移步这里:

算法训练三(DFS、BFS、回溯)(含模板)(上)_清晨喝碗粥的博客-CSDN博客

7-13 桥短几何(深度优先搜索+广度优先搜索)

在一个NxN的布尔矩阵中,0表示水,1表示陆地,一片由1围成的最大区域就是一个岛,假定方阵中有且只有两个岛,请计算连接这两个岛的最短的桥的长度(架桥相当于翻转0为1,使两个岛相连)。

输入样例1:

第一行一个正整数N(取值范围在[2--100])。
以后N行以空格分隔的0或1,每行N个(0表示水,1表示陆地)。

3
0 0 1
0 0 1
1 1 0

输出样例1:

一个表示最短的桥的长度的正整数,本例中,正中间或右下角均是可行方案。

1

输入样例2:

第一行一个正整数N(取值范围在[2--100])。
以后N行以空格分隔的0或1,每行N个(0表示水,1表示陆地)。

5
1 1 1 1 1
1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
1 0 0 0 1

输出样例2:

一个表示最短的桥的长度的正整数,本例中,最后一行是可行方案。

3

思路:可以先用深度优先搜索找到一座岛,然后再用广度优先搜索寻找与另一座岛接邻的最短的桥 

AC代码:

#include<bits/stdc++.h>
using namespace std;
vector<pair<int, int>>dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int dfs(vector<vector<int>> &nums) {
    vector<pair<int, int>>temp;
    queue<pair<int, int>>Q;
    int i, j, k, n;
    for (i = 0; i < nums.size(); i++) {
        for (j = 0; j < nums.size(); j++) {
            if (nums[i][j] == 1) {
                nums[i][j] = -1;
                Q.push(pair<int, int>(i, j));
                while (!Q.empty()) {
                    int new_x, new_y;
                    temp.push_back(pair<int, int>(Q.front().first, Q.front().second));
                    for (auto & dir : dirs) {
                        new_x = Q.front().first + dir.first;
                        new_y = Q.front().second + dir.second;
                        if (new_x >= 0 && new_x < nums.size() && new_y >= 0 && new_y < nums.size() && nums[new_x][new_y] == 1) {
                            Q.push(pair<int, int>(new_x, new_y));
                            nums[new_x][new_y] = -1;
                        }
                    }
                    Q.pop();
                }
                for (auto & a : temp) {
                    Q.push(pair<int, int>(a.first, a.second));
                }
                int step = 0;
                while (!Q.empty()) {
                    int size = Q.size();
                    for (k = 0; k < size; k++) {
                        int x = Q.front().first;
                        int y = Q.front().second;
                        Q.pop();
                        int new_x, new_y;
                        for (auto & dir : dirs) {
                            new_x = x + dir.first;
                            new_y = y + dir.second;
                            if (new_x >= 0 && new_x < nums.size() && new_y >= 0 && new_y < nums.size()) {
                                if (nums[new_x][new_y] == 0) {
                                    Q.push(pair<int, int>(new_x, new_y));
                                    nums[new_x][new_y] = -1;
                                } else if (nums[new_x][new_y] == 1) {
                                    return step;
                                }
                            }
                        }
                    }
                    step++;
                }
            }
        }
    }
    return 0;
}
int main()
{
    int i, j, k, n;
    cin >> n;
    vector<vector<int>>nums(n, vector<int>(n, 0));
    for (i = 0; i < n; i++) {
        for (j = 0; j < n; j++) {
            cin >> nums[i][j];
        }
    }
    cout << dfs(nums) << endl;

    system("pause");
    return 0;
}

7-15 01迷宫(广度优先搜索)

有一个仅由数字0与1组成的n*n格迷宫。若你位于一格0上,那么你可以移动到相邻4格中的某一格1上,同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上。

你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。

输入格式:

第1行为两个正整数n,m。

下面n行,每行n个字符,字符只可能是0或者1,字符之间没有空格。

接下来m行,每行2个用空格分隔的正整数i,j,对应了迷宫中第i行第j列的一个格子,询问从这一格开始能移动到多少格。
(n <= 1000, m <= 100000)

输出格式:

m行,对于每个询问输出相应答案。

输入样例:

2 2
01
10
1 1
2 2

输出样例:

4
4

AC代码:

#include<bits/stdc++.h>
using namespace std;
vector<pair<int, int>>dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
vector<string>G;
vector<vector<int>>visit;
vector<int>item(100005, 0);
int cur = 1;
typedef struct point {
    int x, y;
}point;
int bfs(int start_x, int start_y) {
    point start;
    start.x = start_x;
    start.y = start_y;
    int step = 1;
    queue<point>Q;
    Q.push(start);
    while (!Q.empty()) {
        int x = Q.front().x;
        int y = Q.front().y;
        int new_x, new_y;
        for (auto & dir : dirs) {
            new_x = x + dir.first;
            new_y = y + dir.second;
            if (new_x >= 0 && new_x < G.size() && new_y >= 0 && new_y < G[0].length() && visit[new_x][new_y] == 0) {
                if (G[new_x][new_y] == '0' && G[x][y] == '1' || G[new_x][new_y] == '1' && G[x][y] == '0') {
                    point temp;
                    temp.x = new_x;
                    temp.y = new_y;
                    step++;
                    visit[new_x][new_y] = cur;
                    Q.push(temp);
                }
            }
        }
        Q.pop();
    }
    item[cur] = step;
    cur++;
    return step;
}
int solve() {
    int start_x, start_y;
    cin >> start_x >> start_y;
    start_x -= 1;
    start_y -= 1;
    if (visit[start_x][start_y] != 0)
        return item[visit[start_x][start_y]];
    visit[start_x][start_y] = cur;
    return bfs(start_x, start_y);
}
int main()
{
    int i, j, k, n, m;
    cin >> n >> m;
    G.resize(n);
    visit.resize(n);
    for (i = 0; i < n; i++) {
        visit[i].resize(n, 0);
    }
    for (i = 0; i < n; i++) {
        cin >> G[i];
    }
    vector<int>res;
    while (m--) {
        res.push_back(solve());
    }
    for (i = 0; i < res.size(); i++) {
        cout << res[i] << endl;
    }

    system("pause");
    return 0;
}

7-17 生化危机(图的深度优先搜索)

人类正在经历一场生化危机,许多城市已经被病毒侵袭,这些城市中的人们为了避免感染病毒,计划开车逃往其他没有被病毒入侵的城市(安全城市)。有些城市之间有公路直达,有些没有。虽然他们知道哪些城市是安全的,但是不知道有没有一条安全路径能够到达安全城市(只有该路径上经过的所有城市都是安全的,该路径才是安全路径)。请你编写一个程序帮助他们判断。

输入格式:

输入第一行为三个正整数,分别表示所有城市个数m(m<=100)、安全城市个数n(m<=50)、公路个数k(k<=100)。随后一行给出n个安全城市的编号。随后k行,每一行给出两个整数,表示连接一条公路的两个城市编号。最后一行输入两个整数,分别表示当前所在城市s、目标城市d。每行整数之间都用空格分隔。

输出格式:

若目标城市已被病毒入侵(非安全城市),输出"The city i is not safe!";若目标城市为安全城市且从当前所在城市能够经过一条安全路径到达目标城市,输出"The city can arrive safely!";若目标城市为安全城市但是从当前所在城市没有一条安全路径到达目标城市,输出"The city can not arrive safely!",i为目标城市编号。

输入样例1:

5 2 5
3 4
0 1
0 2
0 4
1 2
2 4
0 4

输出样例1:

The city 4 can arrive safely!

输入样例2:

5 2 5
3 4
0 1
0 2
0 4
1 2
2 4
0 3

输出样例2:

The city 3 can not arrive safely!

输入样例3:

5 2 5
3 4
0 1
0 2
0 4
1 2
2 4
0 1

输出样例3: 

The city 1 is not safe!

 思路:用safe数组保存每个地点是否是安全城市,然后对图进行深度优先搜索即可,如果城市安全且没走过则递归调用

AC代码:

#include<bits/stdc++.h>
using namespace std;
vector<vector<int>>G;
vector<bool>visit;
vector<bool>safe;
bool flag = false;
void dfs(int start, int end) {
    if (start == end)
        flag = true;
    visit[start] = true;
    for (auto & a : G[start]) {
        if (safe[a] && visit[a] == false)
            dfs(a, end);
    }
}
int main()
{
    int i, j, k, n, m;
    cin >> m >> n >> k;
    G.resize(m);
    visit.resize(m, false);
    safe.resize(m, false);
    for (i = 0; i < n; i++) {
        int x, y;
        cin >> x;
        safe[x] = true;
    }
    for (i = 0; i < k; i++) {
        int city1, city2;
        cin >> city1 >> city2;
        G[city1].push_back(city2);
        G[city2].push_back(city1);
    }
    int start, end;
    cin >> start >> end;
    if (safe[end] == false)
        cout << "The city " << end <<" is not safe!" << endl;
    else {
        dfs(start, end);
        if (flag)
            cout << "The city " << end << " can arrive safely!" << endl;
        else
            cout << "The city " << end << " can not arrive safely!" << endl;
    }

    system("pause");
    return 0;
}

7-18 模拟炸弹人(广度优先搜索)

《炸弹人》是HUDSON出品的一款ACT类型游戏,经典的第一作登陆在FC版本,游戏于1983年发行。游戏具体操作是一个机器人放置炸弹来炸死敌人,但也可以炸死自己,还有些增强威力与技能道具增加了游戏的可玩性。
    接下来我们就对《炸弹人》进行一次简化模拟:

首先我们需要一个 N×M 大小的矩阵来存放地图,矩阵中 '*' 代表没有点燃的炸弹,‘0’代表空地,玩家可以自定义炸弹的威力为 p (由于我们是简化模拟,所以 p 只会取 1 或者 2 );像《炸弹人》一样,炸弹可以引发自己上、下、左、右(斜角不算)各 p 格范围内产生爆炸,并且爆炸可以产生连锁反应,问至少需要进行几次引爆才可以让所有的炸弹都爆炸。

例如:
在 5×7 的矩阵

 

 中,当玩家把炸弹的威力 p 设为 1 时至少需要进行 4 次引爆,

而当玩家把炸弹的威力 p 设为 2 时至少需要进行 3 次引爆。

输入格式:

在第一行定义矩阵的大小 n,m,1≤n,m≤10^3 和炸弹的威力 p,p∈{1,2},数字之间用空格隔开

随后 n 行是由‘0’和‘*’两种字符组成的 n×m 矩阵,字符之间用空格隔开。

输出格式:

在一行中输出至少需要进行几次引爆可以让所有的炸弹都爆炸。

输入样例1:

5 7 1
0 0 * * 0 0 0
0 0 * 0 0 0 0
0 0 0 0 0 * 0
0 * * 0 0 0 0
0 0 0 * 0 0 0

输出样例1:

4

输入样例2:

5 7 2
0 0 * * 0 0 0
0 0 * 0 0 0 0
0 0 0 0 0 * 0
0 * * 0 0 0 0
0 0 0 * 0 0 0

输出样例2:

3

AC代码:

#include<bits/stdc++.h>
using namespace std;
vector<vector<char>>G;
typedef struct point {
    int x, y;
}point;
void bfs(int start_x, int start_y, int p) {
    point start;
    start.x = start_x;
    start.y = start_y;
    queue<point>Q;
    Q.push(start);
    while (!Q.empty()) {
        int i, j;
        int x = Q.front().x;
        int y = Q.front().y;
        for (i = x - p; i <= x + p; i++) {
            for (j = y - p; j <= y + p; j++) {
                if (i >= 0 && i < G.size() && j >= 0 && j < G[0].size() && G[i][j] == '*' && (i == x || j == y)) {
                    G[i][j] = '#';
                    point temp;
                    temp.x = i;
                    temp.y = j;
                    Q.push(temp);
                }
            }
        }
        Q.pop();
    }
}
int main()
{
    int i, j, n, m, p, count = 0;
    cin >> n >> m >> p;
    G.resize(n);
    for (i = 0; i < n; i++) {
        G[i].resize(m);
        for (j = 0; j < m; j++) {
            cin >> G[i][j];
        }
    }
    for (i = 0; i < n; i++) {
        for (j = 0; j < m; j++) {
            if (G[i][j] == '*') {
                G[i][j] = '#';
                bfs(i, j, p);
                count++;
            }
        }
    }
    cout << count << endl;

    system("pause");
    return 0;
}

7-19 N皇后问题(回溯)

在N×N格的国际象棋盘上摆放N个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,对于给定的N,求出有多少种合法的放置方法。

输入格式:

一个正整数N≤10,表示棋盘和皇后的数量;

输出格式:

一个正整数,表示对应输入行的皇后的不同放置方法。

输入样例:

8

输出样例:

92

AC代码:

#include<bits/stdc++.h>
using namespace std;
vector<vector<int>>G;
bool isvalid(int row, int col, int n) {
    int i, j;
    for (i = 0; i < row; i++) {
        if (G[i][col] == 1)
            return false;
    }
    for (i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
        if (G[i][j] == 1)
            return false;
    }
    for (i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
        if (G[i][j] == 1)
            return false;
    }
    return true;
}
void backtracking(int row, int n, int &count) {
    if (row == n) {
        count++;
        return;
    }
    for (int col = 0; col < n; col++) {
        if (isvalid(row, col, n)) {
            G[row][col] = 1;
            backtracking(row + 1, n, count);
            G[row][col] = 0;
        }
    }
}
int main()
{
    int i, n, count = 0;
    cin >> n;
    G.resize(n, vector<int>(n, 0));
    backtracking(0, n, count);
    cout << count << endl;

    system("pause");
    return 0;
}

7-20 图着色问题(图的应用)

图着色问题是一个著名的NP完全问题。给定无向图G=(V,E),问可否用K种颜色为V中的每一个顶点分配一种颜色,使得不会有两个相邻顶点具有同一种颜色?

但本题并不是要你解决这个着色问题,而是对给定的一种颜色分配,请你判断这是否是图着色问题的一个解。

输入格式:

输入在第一行给出3个整数V(0<V≤500)、E(≥0)和K(0<K≤V),分别是无向图的顶点数、边数、以及颜色数。顶点和颜色都从1到V编号。随后E行,每行给出一条边的两个端点的编号。在图的信息给出之后,给出了一个正整数N(≤20),是待检查的颜色分配方案的个数。随后N行,每行顺次给出V个顶点的颜色(第i个数字表示第i个顶点的颜色),数字间以空格分隔。题目保证给定的无向图是合法的(即不存在自回路和重边)。

输出格式:

对每种颜色分配方案,如果是图着色问题的一个解则输出Yes,否则输出No,每句占一行。

输入样例:

6 8 3
2 1
1 3
4 6
2 5
2 4
5 4
5 6
3 6
4
1 2 3 3 1 2
4 5 6 6 4 5
1 2 3 4 5 6
2 3 4 2 3 4

输出样例:

Yes
Yes
No
No

思路:1、用map存储方案中的每个节点的颜色

           2、用set进行计数,若与顶点数不相等或颜色数字大于顶点个数则为NO

           3、对每个顶点周围的点进行遍历,若有邻接的两个色块颜色相同则为NO

           4、其他情况则是YES

AC代码:

#include<bits/stdc++.h>
using namespace std;
vector<vector<int>>G;
string solve(map<int, int> &mp) {
    int i, j, n;
    for (i = 1; i < G.size(); i++) {
        for (j = 0; j < G[i].size(); j++) {
            if (mp[G[i][j]] == mp[i])
                return "No";
        }
    }
    return "Yes";
}
int main()
{
    int i, j, k, n, v, e, x;
    cin >> v >> e >> k;
    G.resize(v + 1);
    for (i = 0; i < e; i++) {
        int edge1, edge2;
        cin >> edge1 >> edge2;
        G[edge1].push_back(edge2);
        G[edge2].push_back(edge1);
    }
    cin >> n;
    vector<string>res;
    while (n--) {
        map<int, int>mp;
        set<int>st;
        for (i = 1; i <= v; i++) {
            cin >> x;
            mp[i] = x;
            st.insert(x);
        }
        if (st.size() != k || *st.rbegin() > v)
            res.push_back("No");
        else
            res.push_back(solve(mp));
    }
    for (i = 0; i < res.size(); i++) {
        cout << res[i] << endl;
    }


    system("pause");
    return 0;
}

7-21 排座位(图的应用)

布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。

输入格式:

输入第一行给出3个正整数:N(≤100),即前来参宴的宾客总人数,则这些人从1到N编号;M为已知两两宾客之间的关系数;K为查询的条数。随后M行,每行给出一对宾客之间的关系,格式为:宾客1 宾客2 关系,其中关系为1表示是朋友,-1表示是死对头。注意两个人不可能既是朋友又是敌人。最后K行,每行给出一对需要查询的宾客编号。

这里假设朋友的朋友也是朋友。但敌人的敌人并不一定就是朋友,朋友的敌人也不一定是敌人。只有单纯直接的敌对关系才是绝对不能同席的。

输出格式:

对每个查询输出一行结果:如果两位宾客之间是朋友,且没有敌对关系,则输出No problem;如果他们之间并不是朋友,但也不敌对,则输出OK;如果他们之间有敌对,然而也有共同的朋友,则输出OK but...;如果他们之间只有敌对关系,则输出No way

输入样例:

7 8 4
5 6 1
2 7 -1
1 3 1
3 4 1
6 7 -1
1 2 1
1 4 1
2 3 -1
3 4
5 7
2 3
7 2

输出样例:

No problem
OK
OK but...
No way

 思路:按题目要求输出即可,当两个人是死对头的时候要对图进行遍历,看是否纯在共同朋友关系,最后对应输出

AC代码:

#include<bits/stdc++.h>
using namespace std;
vector<vector<int>>G;
string solve() {
    int i, peo1, peo2;
    cin >> peo1 >> peo2;
    if (G[peo1][peo2] == 1)
        return "No problem";
    if (G[peo1][peo2] == 0)
        return "OK";
    if (G[peo1][peo2] == -1) {
        for (i = 1; i < G.size(); i++) {
            if (G[i][peo1] && G[i][peo2])
                return "OK but...";
        }
    }
    return "No way";
}
int main()
{
    int i, j, k, n, m;
    cin >> n >> m >> k;
    G.resize(n + 1, vector<int>(n + 1, 0));
    for (i = 0; i < m; i++) {
        int peo1, peo2, flag;
        cin >> peo1 >> peo2 >> flag;
        G[peo1][peo2] = flag;
        G[peo2][peo1] = flag;
    }
    vector<string>res;
    while (k--) {
        res.push_back(solve());
    }
    for (i = 0; i < res.size(); i++) {
        cout << res[i] << endl;
    }

    system("pause");
    return 0;
}

7-22 红色警报 (图的连通分量)

战争中保持各个城市间的连通性非常重要。本题要求你编写一个报警程序,当失去一个城市导致国家被分裂为多个无法连通的区域时,就发出红色警报。注意:若该国本来就不完全连通,是分裂的k个区域,而失去一个城市并不改变其他城市之间的连通性,则不要发出警报。

输入格式:

输入在第一行给出两个整数N(0 < N ≤ 500)和M(≤ 5000),分别为城市个数(于是默认城市从0到N-1编号)和连接两城市的通路条数。随后M行,每行给出一条通路所连接的两个城市的编号,其间以1个空格分隔。在城市信息之后给出被攻占的信息,即一个正整数K和随后的K个被攻占的城市的编号。

注意:输入保证给出的被攻占的城市编号都是合法的且无重复,但并不保证给出的通路没有重复。

输出格式:

对每个被攻占的城市,如果它会改变整个国家的连通性,则输出Red Alert: City k is lost!,其中k是该城市的编号;否则只输出City k is lost.即可。如果该国失去了最后一个城市,则增加一行输出Game Over.

输入样例:

5 4
0 1
1 3
3 0
0 4
5
1 2 0 4 3

输出样例:

City 1 is lost.
City 2 is lost.
Red Alert: City 0 is lost!
City 4 is lost.
City 3 is lost.
Game Over.

思路:先用一次深度优先搜索计算出连通分量,然后将被攻占的区域重新赋值为不连通(即图没有相应的边连通),最后在用一次深度优先搜索计算被攻占后的连通分量,然后进行对比,如果连通分量增加了,那么攻占的城市是会改变国家的连通性,失去最后一个城市利用数量进行判断即可 

AC代码:

#include<bits/stdc++.h>
using namespace std;
vector<vector<int>>G;
vector<bool>visit;
void dfs(int u) {
    visit[u] = true;
    for (int i = 0; i < G.size(); i++) {
        if (G[i][u] == 1 && visit[i] == false) 
            dfs(i);
    }
}
void dfs2(vector<bool> &vis, int u) {
    vis[u] = true;
    for (int i = 0; i < G.size(); i++) {
        if (G[i][u] == 1 && vis[i] == false)
            dfs2(vis, i);
    }
}
int main()
{
    int i, j, k, n, m;
    cin >> n >> m;
    G.resize(n, vector<int>(n, 0));
    visit.resize(n, false);
    for (i = 0; i < m; i++) {
        int city1, city2;
        cin >> city1 >> city2;
        G[city1][city2] = 1;
        G[city2][city1] = 1;
    }
    cin >> k;
    vector<int>nums(k, 0);
    for (i = 0; i < k; i++) {
        cin >> nums[i];
    }
    int count1 = 0, count2;
    for (i = 0; i < n; i++) {
        if (visit[i] == false) {
            dfs(i);
            count1++;
        }
    }
    for (i = 0; i < nums.size(); i++) {
        vector<bool>vis(n, false);
        count2 = 0;
        for (j = 0; j < G.size(); j++) {
            G[nums[i]][j] = 0;
            G[j][nums[i]] = 0;
        }
        for (j = 0; j <= i; j++) {
            vis[nums[j]] = true;
        }
        for (j = 0; j < n; j++) {
            if (vis[j] == false) {
                dfs2(vis, j);
                count2++;
            }
        }
        if (count2 > count1)
            cout << "Red Alert: City " << nums[i] <<" is lost!" << endl;
        else
            cout << "City " << nums[i] << " is lost." << endl;
        count1 = count2;
    }
    if (k == n)
        cout << "Game Over." << endl;

    system("pause");
    return 0;
}

7-23 列出连通集(深度优先搜索/广度优先搜索)

给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。

输入格式:

输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。随后E行,每行给出一条边的两个端点。每行中的数字之间用1空格分隔。

输出格式:

按照"{ v1​ v2​ ... vk​ }"的格式,每行输出一个连通集。先输出DFS的结果,再输出BFS的结果。

输入样例:

8 6
0 7
0 1
2 0
4 1
2 4
3 5

输出样例:

{ 0 1 4 2 7 }
{ 3 5 }
{ 6 }
{ 0 1 2 7 4 }
{ 3 5 }
{ 6 }

思路:相应的利用深搜和广搜即可,具体写法可以看代码

AC代码:

#include<bits/stdc++.h>
using namespace std;
vector<vector<int>>G;
vector<bool>visit1, visit2;
void dfs(vector<int> &v, int u) {
    visit1[u] = true;
    v.push_back(u);
    for (int i = 0; i < G.size(); i++) {
        if (visit1[i] == false && G[i][u] == 1) 
            dfs(v, i);
    }
}
void bfs(vector<int> &v, int u) {
    queue<int>Q;
    Q.push(u);
    visit2[u] = true;
    while (!Q.empty()) {
        int index = Q.front();
        v.push_back(index);
        for (int i = 0; i < G.size(); i++) {
            if (G[i][index] == 1 && visit2[i] == false) {
                Q.push(i);
                visit2[i] = true;
            }
        }
        Q.pop();
    }
}
int main()
{
    int i, j, k, n, m;
    cin >> n >> m;
    vector<vector<int>>res1, res2;
    G.resize(n, vector<int>(n, 0));
    visit1.resize(n, false);
    visit2.resize(n, false);
    for (i = 0; i < m; i++) {
        int u1, u2;
        cin >> u1 >> u2;
        G[u1][u2] = 1;
        G[u2][u1] = 1;
    }
    for (i = 0; i < n; i++) {
        vector<int>v;
        if (visit1[i] == false) {
            dfs(v, i);
            res1.push_back(v);
        }
    }
    for (i = 0; i < n; i++) {
        vector<int>v;
        if (visit2[i] == false) {
            bfs(v, i);
            res2.push_back(v);
        }
    }
    for (i = 0; i < res1.size(); i++) {
        cout << "{";
        for (j = 0; j < res1[i].size(); j++) {
            cout << " " << res1[i][j];
        }
        cout << " }" << endl;
    }
    for (i = 0; i < res2.size(); i++) {
        cout << "{";
        for (j = 0; j < res2[i].size(); j++) {
            cout << " " << res2[i][j];
        }
        cout << " }" << endl;
    }

    system("pause");
    return 0;
}

7-25 功夫传人 (广度优先搜索)

一门武功能否传承久远并被发扬光大,是要看缘分的。一般来说,师傅传授给徒弟的武功总要打个折扣,于是越往后传,弟子们的功夫就越弱…… 直到某一支的某一代突然出现一个天分特别高的弟子(或者是吃到了灵丹、挖到了特别的秘笈),会将功夫的威力一下子放大N倍 —— 我们称这种弟子为“得道者”。

这里我们来考察某一位祖师爷门下的徒子徒孙家谱:假设家谱中的每个人只有1位师傅(除了祖师爷没有师傅);每位师傅可以带很多徒弟;并且假设辈分严格有序,即祖师爷这门武功的每个第i代传人只能在第i-1代传人中拜1个师傅。我们假设已知祖师爷的功力值为Z,每向下传承一代,就会减弱r%,除非某一代弟子得道。现给出师门谱系关系,要求你算出所有得道者的功力总值。

输入格式:

输入在第一行给出3个正整数,分别是:N(≤105)——整个师门的总人数(于是每个人从0到N−1编号,祖师爷的编号为0);Z——祖师爷的功力值(不一定是整数,但起码是正数);r ——每传一代功夫所打的折扣百分比值(不超过100的正数)。接下来有N行,第i行(i=0,⋯,N−1)描述编号为i的人所传的徒弟,格式为:

Ki​ ID[1] ID[2] ⋯ ID[Ki​]

其中Ki​是徒弟的个数,后面跟的是各位徒弟的编号,数字间以空格间隔。Ki​为零表示这是一位得道者,这时后面跟的一个数字表示其武功被放大的倍数。

输出格式:

在一行中输出所有得道者的功力总值,只保留其整数部分。题目保证输入和正确的输出都不超过1010。

输入样例:

10 18.0 1.00
3 2 3 5
1 9
1 4
1 7
0 7
2 6 1
1 8
0 9
0 4
0 3

输出样例:

404

思路:利用map嵌套vector来构造这样的一个多叉树结构,我们把每个子树的根节点设置为判断节点如果小于0则为得道者,利用bfs对每一层进行遍历,每遍历一层武功都要折扣,直到遇见得道者,将得道者的武功放大相应求和即可,这里需要注意以下祖师爷有可能也是得道者所以优先判断一下 

AC代码:

#include<bits/stdc++.h>
using namespace std;
map<int, vector<int>>mp;
double bfs(double z, double r) {
    int i, j, k, n;
    double res = 0;
    queue<int>Q;
    if (mp[0][0] < 0)
        return (int)(z * fabs(mp[0][0]));
    for (i = 0; i < mp[0].size(); i++) {
        Q.push(mp[0][i]);
    }
    while (!Q.empty()) {
        int size = Q.size();
        z -= (z * 1.0 * r / 100);
        while (size--) {
            if (mp.find(Q.front()) != mp.end() && mp[Q.front()][0] < 0) {
                res += z * 1.0 * fabs(mp[Q.front()][0]);
                mp[Q.front()][0] = -1;
            }
            if (mp.find(Q.front()) != mp.end() && mp[Q.front()][0] != -1) {
                for (i = 0; i < mp[Q.front()].size(); i++) {
                    Q.push(mp[Q.front()][i]);
                }                
            }
            Q.pop();
        }
    }
    return res;
}
int main()
{
    int i, j, n, res = 0;
    double z, r;
    cin >> n >> z >> r;
    for (i = 0; i < n; i++) {
        int k;
        cin >> k;
        for (j = 0; j < k; j++) {
            int x;
            cin >> x;
            mp[i].push_back(x);
        }
        if (k == 0) {
            int x;
            cin >> x;
            mp[i].push_back(-x);
        }
    }

    res = (int)bfs(z, r);
    cout << res << endl;

    system("pause");
    return 0;
}

7-26 深入虎穴 (广度优先搜索)

著名的王牌间谍 007 需要执行一次任务,获取敌方的机密情报。已知情报藏在一个地下迷宫里,迷宫只有一个入口,里面有很多条通路,每条路通向一扇门。每一扇门背后或者是一个房间,或者又有很多条路,同样是每条路通向一扇门…… 他的手里有一张表格,是其他间谍帮他收集到的情报,他们记下了每扇门的编号,以及这扇门背后的每一条通路所到达的门的编号。007 发现不存在两条路通向同一扇门。

内线告诉他,情报就藏在迷宫的最深处。但是这个迷宫太大了,他需要你的帮助 —— 请编程帮他找出距离入口最远的那扇门。

输入格式:

输入首先在一行中给出正整数 N(<10^5),是门的数量。最后 N 行,第 i 行(1≤i≤N)按以下格式描述编号为 i 的那扇门背后能通向的门:

K D[1] D[2] ... D[K]

其中 K 是通道的数量,其后是每扇门的编号。 

输出格式:

在一行中输出距离入口最远的那扇门的编号。题目保证这样的结果是唯一的。

输入样例:

13
3 2 3 4
2 5 6
1 7
1 8
1 9
0
2 11 10
1 13
0
0
1 12
0
0

输出样例:

12

思路:利用map嵌套vector构造多叉树,然后对树进行广度优先搜索即可,直到搜索到最远的叶子即最终答案 

AC代码:

#include<bits/stdc++.h>
using namespace std;
map<int, vector<int>>mp;
vector<bool>visit;
int bfs() {
    int i, res = 0;
    queue<int>Q;
    for (i = 1; i < visit.size(); i++) {
        if (visit[i] == false) {
            Q.push(i);
            break;
        }
    }
    while (!Q.empty()) {
        res = Q.front();
        if (mp[Q.front()][0] != 0) {
            for (i = 0; i < mp[Q.front()].size(); i++) {
                Q.push(mp[Q.front()][i]);
            }
        }
        Q.pop();
    }
    return res;
}
int main()
{
    int i, j, k, n;
    cin >> n;
    visit.resize(n + 1, false);
    for (i = 1; i <= n; i++) {
        cin >> k;
        if (k == 0)
            mp[i].push_back(0);
        for (j = 0; j < k; j++) {
            int x;
            cin >> x;
            visit[x] = true;
            mp[i].push_back(x);
        }
    }
    cout << bfs() << endl;

    system("pause");
    return 0;
}

7-27 肿瘤诊断 (三维广度优先搜索)

在诊断肿瘤疾病时,计算肿瘤体积是很重要的一环。给定病灶扫描切片中标注出的疑似肿瘤区域,请你计算肿瘤的体积。

输入格式:

输入第一行给出4个正整数:M、N、L、T,其中M和N是每张切片的尺寸(即每张切片是一个M×N的像素矩阵。最大分辨率是1286×128);L(≤60)是切片的张数;T是一个整数阈值(若疑似肿瘤的连通体体积小于T,则该小块忽略不计)。

最后给出L张切片。每张用一个由0和1组成的M×N的矩阵表示,其中1表示疑似肿瘤的像素,0表示正常像素。由于切片厚度可以认为是一个常数,于是我们只要数连通体中1的个数就可以得到体积了。麻烦的是,可能存在多个肿瘤,这时我们只统计那些体积不小于T的。两个像素被认为是“连通的”,如果它们有一个共同的切面,如下图所示,所有6个红色的像素都与蓝色的像素连通。

 输出格式:

在一行中输出肿瘤的总体积。

输入样例:

3 4 5 2
1 1 1 1
1 1 1 1
1 1 1 1
0 0 1 1
0 0 1 1
0 0 1 1
1 0 1 1
0 1 0 0
0 0 0 0
1 0 1 1
0 0 0 0
0 0 0 0
0 0 0 1
0 0 0 1
1 0 0 0

输出样例:

26

思路:这道题仔细看一下会发现是三维的,我们用一个三维数组存储即可,然后利用bfs对其每一块肿瘤进行遍历,如果遍历的肿瘤体积小于给定阈值则忽略,否则就加入到结果变量res中 

AC代码:

#include<bits/stdc++.h>
using namespace std;
vector<vector<vector<int>>>G;
vector<vector<vector<bool>>>visit;
vector<vector<int>>dirs = {{0, 0, 1}, {0, 0, -1}, {0, 1, 0}, {0, -1, 0}, {1, 0, 0}, {-1, 0, 0}};
typedef struct point{
    int x, y, z;
}point;
int bfs(int i, int j, int k, int t) {
    int volume = 1;
    point start;
    start.x = j;
    start.y = k;
    start.z = i;
    queue<point>Q;
    Q.push(start);
    while (!Q.empty()) {
        int x = Q.front().x;
        int y = Q.front().y;
        int z = Q.front().z;
        int new_x, new_y, new_z;
        for (auto & dir : dirs) {
            new_x = x + dir[0];
            new_y = y + dir[1];
            new_z = z + dir[2];
            if (new_z >= 0 && new_z < G.size() && new_x >= 0 && new_x < G[0].size() && new_y >= 0 && new_y < G[0][0].size()) {
                if (G[new_z][new_x][new_y] == 1 && visit[new_z][new_x][new_y] == false) {
                    visit[new_z][new_x][new_y] = true;
                    point temp;
                    temp.x = new_x;
                    temp.y = new_y;
                    temp.z = new_z;
                    Q.push(temp);
                    volume++;
                }
            }
        }
        Q.pop();
    }
    if (volume < t)
        return 0;
    return volume;
}
int main()
{
    int i, j, k, n, m, l, t, res = 0;
    cin >> m >> n >> l >> t;
    G.resize(l, vector<vector<int>>(m, vector<int>(n, 0)));
    visit.resize(l, vector<vector<bool>>(m, vector<bool>(n, false)));
    for (i = 0; i < l; i++) {
        for (j = 0; j < m; j++) {
            for (k = 0; k < n; k++) {
                cin >> G[i][j][k];
            }
        }
    }
    for (i = 0; i < l; i++) {
        for (j = 0; j < m; j++) {
            for (k = 0; k < n; k++) {
                if (visit[i][j][k] == false && G[i][j][k] == 1) {
                    visit[i][j][k] = true;
                    res += bfs(i, j, k, t);
                }
            }
        }
    }
    cout << res << endl;

    system("pause");
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值