寒假c++学习第六弹——深度优先搜索(2021.02.02)
目录
迷宫最短路径
1.踏青
2.迷宫解方案数
3.最大的蛋糕块
4.家谱
5.马的覆盖点
6.王子救公主
7.蒜头君开公司
dfs求走出迷宫最短路径问题
//迷宫问题求最短路径问题(m行,n列的迷宫,1代表可走,2代表不可走,出发坐标(startx,starty),目标坐标(p,q))
#include <iostream>
using namespace std;
int maze[10][10]; //迷宫数组
int vis[10][10]; //访问数组,已访问为1,未访问为0
int mins = 100;
int m, n, startx, starty, p, q;
void dfs(int x, int y, int step){
if(x == p && y == q){
if(step < mins){
mins = step;
return;
}
}
//向下
if(maze[x+1][y] == 1 && vis[x+1][y] == 0){
vis[x+1][y] = 1;
dfs(x+1, y, step+1);
vis[x+1][y] = 0; //搜索完后恢复为0,以便下次搜索
}
//向左
if(maze[x][y-1] == 1 && vis[x][y-1] == 0){
vis[x][y-1] = 1;
dfs(x, y-1, step+1);
vis[x][y-1] = 0;
}
//向上
if(maze[x-1][y] == 1 && vis[x-1][y] == 0){
vis[x-1][y] = 1;
dfs(x-1, y, step+1);
vis[x-1][y] = 0;
}
//向右
if(maze[x][y+1] == 1 && vis[x][y+1] == 0){
vis[x][y+1] = 1;
dfs(x, y+1, step+1);
vis[x][y+1] = 0;
}
return;
}
int main()
{
cin >> m >> n;
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
cin >> maze[i][j];
}
}
cin >> startx >> starty >> p >> q;
vis[startx][starty] = 1;
dfs(startx, starty, 0);
cout << mins << endl;
return 0;
}
1.踏青
题目描述:
蒜头君和他的朋友周末相约去召唤师峡谷踏青。他们发现召唤师峡谷的地图是由一块一块格子组成的,有的格子上是草丛,有的是空地。草丛通过上下左右 4 个方向扩展其他草丛形成一片草地,任何一片草地中的格子都是草丛,并且所有格子之间都能通过上下左右连通。如果用'#'
代表草丛,'.'
代表空地,下面的峡谷中有 2 片草地。
##..
..##
处在同一个草地的 2 个人可以相互看到,空地看不到草地里面的人。他们发现有一个朋友不见了,现在需要分头去找,每个人负责一片草地,蒜头君想知道他们至少需要多少人。
输入格式:
第一行输入 n, m(1≤n,m≤100) 表示峡谷大小。
接下来输入 n 行字符串表示峡谷的地形。
输出格式:
输出至少需要多少人。
输入样例:
5 6 .#.... ..#... ..#..# ...##. .#....
输出样例:
5
参考代码
//本质是搜索有多少块草地
#include <iostream>
using namespace std;
int n, m, cnt=0;
char a[100][100]; //峡谷地图
bool vis[100][100]; //访问数组
void dfs(int x, int y){
if(x >= n || y >= m || x < 0 || y < 0 || vis[x][y] || a[x][y] == '.'){
return;
}
vis[x][y] = true;
dfs(x+1, y);
dfs(x, y-1);
dfs(x-1, y);
dfs(x, y+1);
}
int main(){
cin >> n >> m;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
cin >> a[i][j];
}
}
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(!vis[i][j] && a[i][j] == '#'){
dfs(i, j);
cnt++;
}
}
}
cout << cnt << endl;
return 0;
}
2.迷宫解方案数
题目描述:
蒜头君是一个玩迷宫的高手,天下还没有能难住他的迷宫。但是总有人喜欢刁难蒜头君,不停的给蒜头君出难题。这个出题的人很聪敏,他知道天下还没有能难住蒜头君的迷宫。所以他便转换思维问蒜头君,在不走重复路径的情况下,总共有多少不同可以到达终点的路径呢?蒜头君稍加思索便给出了答案,你要不要也来挑战一下?
输入格式:
第一行输入两个整数 n(1 ≤ n ≤ 11), m(1 ≤ m ≤ 11).表示迷宫的行和列。
然后有一个 n × m 的地图,地图由’.’、’#’、‘s’、‘e’这四个部分组成。’.‘表示可以通行的路,’#'表示迷宫的墙,'s’表示起始点,'e’表示终点。
输出格式:
输出一个整数,表示从’s’到达’e’的所有方案数。
输入样例:
5 5
s####
.####
.####
.####
….e
输出样例:
1
参考代码
//dfs求所有方案数
#include <iostream>
using namespace std;
char maze[10][10]; //迷宫数组
int vis[10][10]; //访问数组,已访问为1,未访问为0
int n, m, x, y, cnt=0;
void dfs(int x, int y){
if(x >= n || y >= m || x < 0 || y < 0 || vis[x][y] || maze[x][y] == '#'){
return;
}
if(maze[x][y] == 'e'){
cnt++;
return;
}
vis[x][y] = 1;
//向下
dfs(x+1, y);
//向左
dfs(x, y-1);
//向上
dfs(x-1, y);
//向右
dfs(x, y+1);
vis[x][y] = 0;
return;
}
int main()
{
cin >> m >> n;
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
cin >> maze[i][j];
}
}
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
if(maze[i][j] == 's'){
x = i;
y = j;
}
}
}
dfs(x, y);
cout << cnt << endl;
return 0;
}
3.最大的蛋糕块
题目描述:
这一天蒜头君生日,他的朋友们一起来给蒜头君买一个大的蛋糕过生日。游戏做完后到了切蛋糕的时刻了,朋友们知道蒜头君喜欢吃蛋糕,便让蒜头君自己给自己切一块最大的。蒜头君看朋友们这么热情也就不客气了。
这块蛋糕是由 R \times CR×C 的网格构成,每个网格上面都放有不同的水果。蒜头君把这些水果分为两类,一类是自己喜欢吃的水果,用’#‘来表示;一类是自己不喜欢吃的水果,用’.'来表示。
蒜头君对切出的蛋糕有如下要求:
切出的蛋糕连成一块(可以不为矩形,但必须在网格上连通)
切出的蛋糕只包含自己喜欢吃的水果
请问,蒜头君最大可以吃到多大的蛋糕?
输入格式:
第一行输入两个被空格隔开的整数 R(1 \le R \le 1000)R(1≤R≤1000) 和 C(1 \le C \le 1000)C(1≤C≤1000)。
然后会有一个 R \times CR×C 的网格,由’#‘和’.'组成。
输出格式:
输出一个整数,表示蒜头君可以吃到的蛋糕最大是多少(即对应到网格中的格子数)。
输入样例:
5 6
.#....
..#...
..#..#
...##.
.#....
输出样例:
2
参考代码
//求最大联通
#include <iostream>
using namespace std;
int n, m, cnt, ans=0;
char a[100][100];
int vis[100][100];
void dfs(int x, int y){
if(x >= n || y >= m || x < 0 || y < 0 || vis[x][y] || a[x][y] == '.'){
return;
}
vis[x][y] = 1;
cnt++;
//向下
dfs(x+1, y);
//向左
dfs(x, y-1);
//向上
dfs(x-1, y);
//向右
dfs(x, y+1);
return;
}
int main()
{
cin >> n >> m;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
cin >> a[i][j];
}
}
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(!vis[i][j] && a[i][j] == '#'){
cnt = 0;
dfs(i, j);
if(cnt > ans){
ans = cnt;
}
}
}
}
cout << ans;
return 0;
}
4.家谱
题目描述:
输入一个n,接下来有n-1行。
输入父亲和儿子
求n个人,每个人的直系后代有多少。
输入格式:
输入一个n,接下来有n-1行。
输入父亲和儿子
求n个人,每个人的直系后代有多少。
输出格式:
输出n行,每行一个整数,表示第i个人有多少个直系后代
输入样例:
4
1 2
1 3
2 4
输出样例:
3
1
0
0
参考代码
#include <iostream>
#include <vector>
using namespace std;
vector<int> son[100005];
bool f[100005];
int ans[100005];
int dfs(int u){
int ret = 0;
for(int i=0; i<son[u].size(); i++){
ret += dfs(son[u][i]);
}
ans[u] = ret;
return ret + 1;
}
int main()
{
int n, x, y, u;
cin >> n;
for(int i=1; i<=n-1; i++){
cin >> x >> y;
son[x].push_back(y);
f[y] = true;
}
for(int i=1; i<=n; i++){
if(!f[i]){
u = i;
break;
}
}
dfs(u);
for(int i=1; i<=n; i++){
cout << ans[i] << endl;
}
return 0;
}
5.马的覆盖点
题目描述:
已知马走日,可以走八个方向,现用 · 表示可走的路,# 表示到达的点。
给出马的一个初始位置,请输出马在三步内可以到达的点(图来表示)。
输入格式:
第一行输入两个整数n(1 <= n <= 100), m(1 <= m <= 100)代表棋盘行数和列数,第二行输入两个整数x, y(不越界)代表马的初始位置。
输出格式:
输出整个棋盘,'.'表示棋盘上可以落子的点,'#'表示马走三步可以到达的点。
输入样例:
10 9
10 1
输出样例:
.........
.........
.........
.#.#.....
#.#.#....
####.#...
#####.#..
##.###...
#.###.#..
######...
参考代码
#include <iostream>
using namespace std;
char s[105][105];
int n, m;
int dir[8][2] = {{-2, -1}, {-2, 1}, {2, -1}, {2, 1}, {1, 2}, {1, -2}, {-1, 2}, {-1, -2}};
void dfs(int x, int y, int step){
if(step > 3){
return;
}
if(x < 0 || x >= n || y < 0 || y >= m){
return;
}
s[x][y] = '#';
for(int i=0; i<8; i++){
dfs(x+dir[i][0], y+dir[i][1], step+1);
}
}
int main()
{
int x, y;
cin >> n >> m >> x >> y;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
s[i][j] = '.';
}
}
dfs(x-1, y-1, 0);
for(int i=0; i<n; i++){
cout << s[i] << endl;
}
return 0;
}
6.王子救公主
题目描述:
一天,蒜头君梦见自己当上了王子,但是不幸的是,自己的公主被可恶的巫婆抓走了。于是蒜头君动用全国的力量得知,自己的公主被巫婆抓进一个迷宫里面。由于全国只有蒜头君自己可以翻越迷宫外的城墙,蒜头君便自己一人走上的拯救自己公主的路途。
碰巧的是巫婆出去了,迷宫也不大,蒜头君可以直接和公主对话,于是两个人便开始相互靠近。每一步移动只能朝着上下左右四个方向走一格,不能走进墙所在的位置。蒜头君救公主心切,一次必须沿着一个方向走两步(允许跨越迷宫中的墙);公主柔弱,一次只能走一步。问在这个迷宫中,蒜头君是否可以救出公主(蒜头君和公主相遇后,就能背着公主逃出迷宫了)。
输入格式:
第一行输入两个整数 n(1≤n≤100), m(1≤m≤100) 表示迷宫的行和列。
然后有一个 n×m 的地图,地图由'.'
、'#'
、'w'
、'g'
这四个部分组成。'.'
表示可以通行的路,'#'
表示迷宫的墙,'w'
表示王子开始所在的位置,'g'
表示公主开始所在的位置。
输出格式:
输出王子是不可以救出自己的公主,如果能救出则输出"yes"
,否则输出"no"
。
输入样例:
1 8 w....#.g
输出样例:
yes
参考代码
//需要两次搜索,分别表示王子和公主走过的路,然后看看是否他们可以走过同一个位置,如果可以,说明可以救出
#include <iostream>
using namespace std;
char mp[105][105];
bool vis[105][105][2];
int n, m;
void dfs(int x, int y, int d){
if(x < 0 || x>=n || y<0 || y>=m || vis[x][y][d] || mp[x][y] == '#'){
return;
}
//d=0为王子,d=1为公主
vis[x][y][d] = true;
dfs(x - (2-d), y, d);
dfs(x + (2-d), y, d);
dfs(x, y - (2-d), d);
dfs(x, y + (2-d), d);
}
int main()
{
int x, y;
bool ans;
cin >> n >> m;
for(int i=0; i<n; i++){
cin >> mp[i];
}
//王子
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(mp[i][j] == 'w'){
x = i;
y = j;
}
}
}
dfs(x, y, 0);
//公主
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(mp[i][j] == 'g'){
x = i;
y = j;
}
}
}
dfs(x, y, 1);
ans = false;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(vis[i][j][0] && vis[i][j][1]){
ans = true;
}
}
}
if(ans){
cout << "yes" << endl;
}
else{
cout << "no" << endl;
}
return 0;
}
7.蒜头君开公司
题目描述:
2020 年,蒜头君自己开了一家拥有 N 个员工的大公司。每天,蒜头君都要分配 N 项工作给他的员工,但是,由于能力的不同,每个人对处理相同工作所需要的时间有快有慢。众所周知,蒜头君是一个非常重视效率的人,他想知道该如何分配工作,才能使得完成所有工作的时间总和最小(每个员工只可以被分配到一个工作)。但是我们也都知道蒜头君不是一般的懒,所以蒜头君找到了你,请你拯救一下蒜头君吧!
输入格式:
第一行输入一个整数 N,代表有 N 个员工,员工编号从 1 到 N。(1≤N≤10)
接着输入一个 N∗N的二维矩阵 task[N][N],task[i][j] 指的是第 i 项工作如果由 j 号员工完成所需要的时间。(0≤task[i][j]≤1000)
输出格式:
输出结果包括一个整数,代表所需要的最少时间(求和)。
输入样例:
6
10 11 12 11 9 11
11 9 10 13 11 12
12 10 11 10 13 9
9 14 9 10 10 11
10 10 9 11 12 11
10 7 10 10 10 8
输出样例:
54
参考代码
#include <iostream>
using namespace std;
int task[15][15];
//每一列代表一个员工
bool used[15];
int ans; //最少时间总和
int n;
void dfs(int x, int t){ //员工与当前所用时间
if(x == n){
if(t < ans){
ans = t;
}
return;
}
for(int i=0; i<n; i++){
if(!used[i]){
used[i] = true;
dfs(x + 1, t + task[x][i]);
used[i] = false;
}
}
}
int main()
{
cin >> n;
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
cin >> task[i][j];
}
}
ans = 20000;
dfs(0, 0);
cout << ans;
return 0;
}
如有错误欢迎指正,题目来源:计蒜客