「基础算法」第5章 广度搜索课堂过关

「基础算法」第5章 广度搜索课堂过关

A. 【例题1】走迷宫

题目

思路

广搜裸题,略

代码

#include <iostream>
#include <cstdio>
using namespace std;
bool end;

const int f[4][2] = {0,1 , 0,-1 , 1,0 , -1,0};

int h[2] , t[2];
int q[2][500010][2];

int dis[1010][1010];
int n;
int sx , sy , ex , ey;
bool map[1010][1010];
inline int abs_(int x){return x < 0 ? -x : x;}
void bfs(int id) {
	if(end)return;
	int sig = (id ? -1 : 1);
	int x = q[id][h[id]][0] , y = q[id][h[id]][1];
//	cout << x << '\t' << y << endl;
	for(int i = 0 ; i < 4 ; i++) {
		int gx = x + f[i][0] , gy = y + f[i][1];
		if((gx == sx && gy == sy) || (gx == ex && gy == ey))continue;
		if(gx <= 0 || gy <= 0 || gx > n || gy > n || !map[gx][gy])continue;
		if(dis[gx][gy] != 0) {
			if(dis[gx][gy] * dis[x][y] < 0) {
				cout << abs_(-dis[gx][gy] + dis[x][y]) + 1 << endl;
				end = true;
				return;
			}
			else	continue;
		}
		q[id][t[id]][0] = gx , q[id][t[id]][1] = gy;
		dis[gx][gy] = sig + dis[x][y];
		t[id]++;
	}
	h[id]++;
	
/*	cout << endl;
	for(int i = 1 ; i <= n ; i++) {
		for(int j = 1 ; j <= n ; j++)
			cout << dis[i][j] << '\t';
		cout << endl;
	}
	cout <<endl;*/
}
void ctrl() {
	h[0] = h[1] = 0;
	t[0] = t[1] = 1;
	q[0][0][0] = sx , q[0][0][1] = sy;
	q[1][0][0] = ex , q[1][0][1] = ey;
	while(!end)
		bfs(t[0] > t[1] ? 1 : 0);
}
int main() {
	cin >> n;
	for(int i = 1 ; i <= n ; i++)
		for(int j = 1 ; j <= n ; j++) {
			char c = getchar();
			while(c != '0' && c != '1') c = getchar();
			map[i][j] = (c == '0' ? true : false);
		}
	cin >> sx >> sy >> ex >> ey;
	ctrl();
/*	cout << endl;
	for(int i = 1 ; i <= n ; i++) {
		for(int j = 1 ; j <= n ; j++)
			cout << dis[i][j] << '\t';
		cout << endl;
	}*/
	return 0;
}

B. 【例题2】山峰和山谷

题目

思路

广搜次裸题,略

代码

随机数据

#include <bits/stdc++.h>
using namespace std;
int random(int r , int l = 1) {
	return (long long) rand() * rand() * rand() % (r - l + 1) + l;
}
int main() {
	unsigned seed;
	cin >> seed;
	seed *= time(0);
	srand(seed);
	
	int n = random(100);
	
	cout << n << endl;
	for(int i = 1 ; i <= n ; i++) {
		for(int j = 1 ; j <= n ; j++)
			printf("%d " , random(n));
		putchar('\n');
	}
	return 0;
}

AC代码

#include <iostream>
#include <cstdio>
#define nn 1010
using namespace std;
int read() {
	int re = 0 , sig = 1;
	char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-')	sig = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0',
		c = getchar();
	return re * sig;
}

int n , h[nn][nn];
bool vis[nn][nn];
const int f[8][2] = {0,1 , 0,-1 , 1,0 , -1,0 , 1,1 , -1,1 , -1,-1 , 1,-1};

int dfs(int x , int y) {
	int ty = 0;
	vis[x][y] = true;
	for(int i = 0 ; i < 8 ; i++) {
		int gx = x + f[i][0] , gy = y + f[i][1];
		if(gx <= 0 || gx > n || gy <= 0 || gy > n)
			continue;
		if(h[x][y] != h[gx][gy]) {
			if(h[x][y] > h[gx][gy]) {
				if(ty == 2)			ty = -1;
				else if(ty != -1)	ty = 1;
			}
			else {
				if(ty == 1) 		ty = -1;
				else if(ty != -1)	ty = 2;
			}
			continue;
		}
		if(vis[gx][gy])	continue;
		int sty = dfs(gx , gy);
		
		if(sty == 0)	continue;
		if(ty == 0)		ty = sty;
		else	 		if(ty != sty)	ty = -1;
	}
	return ty;
}
int ans1 , ans2;
int main() {
	n = read();
	for(int i = 1 ; i <= n ; i++)
		for(int j = 1 ; j <= n ; j++)
			h[i][j] = read();
	
	for(int i = 1 ; i <= n ; i++)
		for(int j = 1 ; j <= n ; j++) {
			if(vis[i][j])	continue;
			int ty = dfs(i , j);
			if(ty == 1) {
				ans1++;
			}
			else	if(ty == 2)	ans2++;
			else	if(ty == 0) {
				ans1++ , ans2++;
			}
		}
	cout << ans1 << ' ' << ans2;
	return 0;
}

C. 【例题3】立体推箱子

题目

思路

其实木块可以直接看成三种状态:立着,横躺,竖躺,分别记为:S(stand),R(right),D(down) (英文不好别见怪)

#define R 0
#define D 1
#define S 2

用x,y表示是木块的坐标,s表示状态(当s=R时,(x,y)表示木块所处两个点中位于左边的点的坐标,s=D时,(x,y)表示木块所处两个点中位于下面的点的坐标)

写出一个check函数判断(x,y,s)是否合法:

inline bool check(int x , int y , int s) {//map[i][j]:	1硬地(或起点终点)	2易碎地面	3禁地
	if(x <= 0 || y <= 0 || x > n || y > m)return false;
	if(map[x][y] == 2)	return false;
	if(s == S)	return map[x][y] == 1;
	int gx , gy;
	if(s == D)	gx = x + 1 , 	gy = y;
	if(s == R)	gx = x,		gy = y + 1;
	if(gx <= 0 || gy <= 0 || gx > n || gy > m|| map[gx][gy] == 2)	return false;
	return true;
}

写出f数组:

const int f[3][4][3] = {//{delta_x , delta_y , s}
{{0,2,S} , {-1,0,R} , {1,0,R} , {0,-1,S}},//s=R时x,y,s对应的变化情况 (分别为向左滚动箱子,向下,向上,向右)
{{2,0,S} , {-1,0,S} , {0,-1,D} , {0,1,D}},//s=D同理
{{-2,0,D} , {1,0,D} , {0,-2,R} , {0,1,R}}//s=S同理
};

BFS代码就很简单啦

void bfs() {
	memset(dis , -1 , sizeof(dis));//多组数据记得清空
	q.clear();
	q.push(sx , sy , ss);//起点入队
	dis[sx][sy][ss] = 0;
	while(!empty(q)) {
		node p = q.front();
		q.h++;
		for(int i = 0 ; i < 4 ; i++) {
			int gx = p.x + f[p.s][i][0] , gy = p.y + f[p.s][i][1] , gs = f[p.s][i][2];//(gx,gy,gs)表示变化后的坐标和状态
			if(!check(gx , gy , gs))	continue;
			if(dis[gx][gy][gs] != -1)	continue;
			if(gx == ex && gy == ey && gs == S) {
				cout << dis[p.x][p.y][p.s] + 1 << endl;
				return;
			}
			dis[gx][gy][gs] = dis[p.x][p.y][p.s] + 1;
			q.push(gx , gy , gs);
		}
	}
	puts("Impossible");
}

完整代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define R 0
#define D 1
#define S 2
#define nn 510
struct node {
	int x , y , s;
};
struct quenode {
	int h , t;
	node q[nn * nn * 3];
	inline void push(int _x , int _y , int _s) {
		node tmp;
		tmp.x = _x , tmp.y = _y , tmp.s = _s;
		q[t] = tmp;
		++t;
	}
	inline node front() {
		return q[h];
	}
	inline void clear() {
		h = t = 0;
		memset(q , 0 , sizeof(q));
	}
}q;
#define empty(q) (q.h == q.t)


int n , m;
int sx , sy , ex , ey , ss;
int map[nn][nn];
int dis[nn][nn][5];

inline bool check(int x , int y , int s) {
	if(x <= 0 || y <= 0 || x > n || y > m)return false;
	if(map[x][y] == 2)	return false;
	if(s == S)	return map[x][y] == 1;
	int gx , gy;
	if(s == D)	gx = x + 1 , 	gy = y;
	if(s == R)	gx = x,		gy = y + 1;
	if(gx <= 0 || gy <= 0 || gx > n || gy > m|| map[gx][gy] == 2)	return false;
	return true;
}
const int f[3][4][3] = {//{delta_x , delta_y , s}
{{0,2,S} , {-1,0,R} , {1,0,R} , {0,-1,S}},//R
{{2,0,S} , {-1,0,S} , {0,-1,D} , {0,1,D}},//D
{{-2,0,D} , {1,0,D} , {0,-2,R} , {0,1,R}}//S
};
void bfs() {
	memset(dis , -1 , sizeof(dis));
	q.clear();
	q.push(sx , sy , ss);
	dis[sx][sy][ss] = 0;
	while(!empty(q)) {
		node p = q.front();
		q.h++;
		for(int i = 0 ; i < 4 ; i++) {
			int gx = p.x + f[p.s][i][0] , gy = p.y + f[p.s][i][1] , gs = f[p.s][i][2];
			if(!check(gx , gy , gs))	continue;
			if(dis[gx][gy][gs] != -1)	continue;
			if(gx == ex && gy == ey && gs == S) {
				cout << dis[p.x][p.y][p.s] + 1 << endl;
				return;
			}
			dis[gx][gy][gs] = dis[p.x][p.y][p.s] + 1;
			q.push(gx , gy , gs);
		}
	}
	puts("Impossible");
}
int main() {
	while(1) {
		cin >> n >> m;
		if(n == 0 && m == 0)return 0;
		bool readx = false;
		for(int i = 1 ; i <= n ; i++)
			for(int j = 1 ; j <= m ; j++) {
				char c;
				bool flag; 
				do {
					c = getchar();
					flag = true;
					switch(c) {
						case '.':
							map[i][j] = 1;
							break;
						case 'E':
							map[i][j] = 3;
							
							break;
						case '#':
							map[i][j] = 2;
							
							break;
						case 'X':
							if(readx) {
								ss = (sx == i ? R : D);
							}
							else {
								sx = i , sy = j;
								ss = S;
								readx = true;
							}
							map[i][j] = 1;
							break;
						case 'O':
							map[i][j] = 1;
							ex = i , ey = j;
							break;
						
						default :
							flag = false;
							break;
					}
				}while(!flag);
			}
//			cout << sx << '\t' << sy << endl;
		bfs();
		/*
		for(int i = 1 ; i <= n ; i++) {
			for(int j = 1 ; j <= m ; j++) {
				cout << map[i][j] <<' ';
			}
			cout << endl;
		}*/
	}
	
	
	return 0;
}

D. 【例题4】荆轲刺秦王

待做(T_T)

E. 【例题5】电路维修

题目

思路

对于每一个正方形元件,有四种情况:

连接电源的一端为:左上 右下 左下 右上,分别用1,2,3,4表示,向推箱子一样,先写出f数组:

const int stdf[10][4][3] = {//{delta_x , delta_y , st}
	{{0}},
	{{1,0,4} , {1,1,1} , {0,1,3}	},
	{{-1,0,3} , {-1,-1,2} , {0,-1,4}	},
	{{-1,1,3} , {-1,0,2} , {0,1,1}	},
	{{1,0,1} , {1,-1,4} , {0,-1,2}	}
};

广搜:

注意下左上角的点状态一定要为“1”,右下角的状态也要为“1”


	map[0][0] = 0;
	q.push(mak(0,0,1,0));
	while(!q.empty()) {
		node t = q.top();
		q.pop();
		if(dis[t.x][t.y][t.st] != -1)continue;
		
//		if(t.x == 1 && t.y == 1)
//			cout << t.x << '\t' <<t.y << '\t' << t.st << '\t' << t.step << '\n';
		
		if(t.x == n && t.y == m && t.st == 1) {
			cout << t.step << endl;
			return;
		}

		dis[t.x][t.y][t.st] = t.step;

		int f[4][3];
		for(int i = 0 ; i < 3 ; i++)
			for(int j = 0 ; j < 3 ; j++)
				f[i][j] = stdf[t.st][i][j];

		for(int i = 0 ; i < 3 ; i++) {
			int gx = f[i][0] + t.x , gy = f[i][1] + t.y , gt = f[i][2];
			if(gx <= 0 || gy <= 0 || gx > n || gy > m)	continue;
			if(dis[gx][gy][gt] != -1)	continue;
			q.push(mak(gx , gy , gt , t.step + (map[gx][gy] == 0 ? (gt == 1 || gt == 2 ? 0 : 1) : (gt == 1 || gt == 2 ? 1 : 0) )) );
		}
	}
	puts("NO SOLUTION");

代码

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
#define nn 510

int n , m;
bool map[nn][nn];
int dis[nn][nn][5];
struct node {
	int x , y , st;//st:	1:左上	 2:右下 	3: 左下		4: 右上(source)
	int step;
	bool operator < (const node &b) const {
		return step > b.step;
	}
};
node mak(int x , int y , int st , int step) {
	node tmp;
	tmp.x = x , tmp.y = y , tmp.st = st , tmp.step = step;
	return tmp;
}


const int stdf[10][4][3] = {//{delta_x , delta_y , st}
	{{0}},
	{{1,0,4} , {1,1,1} , {0,1,3}	},
	{{-1,0,3} , {-1,-1,2} , {0,-1,4}	},
	{{-1,1,3} , {-1,0,2} , {0,1,1}	},
	{{1,0,1} , {1,-1,4} , {0,-1,2}	}
};

void work () {
	memset(map , 0 , sizeof(map));
	memset(dis , -1 , sizeof(dis));
	priority_queue <node> q;
	
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			char c = getchar();
			while (c != '\\' && c != '/') c = getchar();
			map[i][j] = (c == '/' ? 1 : 0);
		}
	map[0][0] = 0;
	q.push(mak(0,0,1,0));
	while(!q.empty()) {
		node t = q.top();
		q.pop();
		if(dis[t.x][t.y][t.st] != -1)continue;
		
//		if(t.x == 1 && t.y == 1)
//			cout << t.x << '\t' <<t.y << '\t' << t.st << '\t' << t.step << '\n';
		
		if(t.x == n && t.y == m && t.st == 1) {
			cout << t.step << endl;
			return;
		}

		dis[t.x][t.y][t.st] = t.step;

		int f[4][3];
		for(int i = 0 ; i < 3 ; i++)
			for(int j = 0 ; j < 3 ; j++)
				f[i][j] = stdf[t.st][i][j];

		for(int i = 0 ; i < 3 ; i++) {
			int gx = f[i][0] + t.x , gy = f[i][1] + t.y , gt = f[i][2];
			if(gx <= 0 || gy <= 0 || gx > n || gy > m)	continue;
			if(dis[gx][gy][gt] != -1)	continue;
			q.push(mak(gx , gy , gt , t.step + (map[gx][gy] == 0 ? (gt == 1 || gt == 2 ? 0 : 1) : (gt == 1 || gt == 2 ? 1 : 0) )) );
		}
	}
	puts("NO SOLUTION");
}
int main() {
	int T;
	cin >> T;
	while(T--) {
		work();
	}

	return 0;
}

F. 【例题6】逃离噩梦

题目

思路

不难想但是有点难写的一道搜索题,简单点的做法就是直接从男生女生出发跑两遍BFS,算出距离,最后再枚举所有点得出答案,这里不详细展开

另外就是可以练下双向BFS,详细细节见下面代码

代码(双向BFS)

#include <iostream>
#include <cstdio>
#include <cstring>
#define nn 1010
using namespace std;
inline int read() {
	int x = 0, f = 1; char s = getchar();
	while (s < '0' || s > '9') { if (s == '-') f = -f; s = getchar(); }
	while (s >= '0' && s <= '9') { x = x * 10 + s - '0'; s = getchar(); }
	return x * f;
}

struct point {
	int x , y;
}man , gir , z1 , z2;
//队列
#define getp(p) (p.x = i , p.y = j)
#define empty_(q) (q.h == q.t)
#define pop_(q) (++q.h)
#define siz(q) (q.t - q.h)
#define head(q_) q_.q[q_.h]
struct que {
	int h , t;
	point q[nn * nn];
	inline void push(int x , int y) {
		q[t].x = x , q[t].y = y;
		++t;
	}
	inline void push(point p) {
		q[t++] = p;
	}
	inline void clear() {
		memset(q , 0 , sizeof(q));
		h = t = 0;
	}
	void print() {
		for(int i = h ; i < t ; i++)
			cout << q[i].x << ',' << q[i].y << '\t';
		cout <<endl;
	}
}q[3];
//==========队列END

int n , m;
int vis[nn][nn];//vis记录各个点到达距离,负数记录男生,正数记录女生
char map[nn][nn];//地图

//四向移动,两个数组是因为一开始对题意理解有误,可忽略
const int f[5][30][2] = 
{
	{{4 , 4}},
	{{0,1} , {1,0} , {0 , -1} , {-1 , 0} },
	{{0,1} , {1,0} , {0 , -1} , {-1 , 0} }
};

#define abs_(_) ((_) < 0 ? -(_) : (_))
inline int max_(int a , int b) {
	return a > b ? a : b;
}
int bfs(int peo) {//搜索,peo==1表示男生,2表示女生
	point p = head(q[peo]);
	pop_(q[peo]);
	
	int t = vis[p.x][p.y] + (peo == 1 ? -1 : 1);
	if(t < 0) {//计算到达当前点时间
		t = -t;	t = t / 3 + (t % 3 == 0 ? 0 : 1);
	}
	if(abs_(p.x - z1.x) + abs_(p.y - z1.y) <= 2 * t || abs_(p.x - z2.x) + abs_(p.y - z2.y) <= 2 * t)//鬼已经扩展到当前点,舍去
		return -1;
    
	for(int i = 1 ; i <= 2 ; i++) {//BFS常规
		for(int j = 0 ; j < f[0][0][i - 1] ; j++) {
			int x = p.x + f[i][j][0] , y = p.y + f[i][j][1];
			if(abs_(x - z1.x) + abs_(y - z1.y) <= 2 * t || abs_(x - z2.x) + abs_(y - z2.y) <= 2 * t)
				continue;
			if(map[x][y] == 'M' && peo == 2 || map[x][y] == 'G' &&peo == 1)	return t;
			if(x < 0 || x > n || y < 0 || y > m || map[x][y] != '.')	continue;
			
			
			if(vis[x][y] != 0) {
				if((peo == 1 ? -1 : 1) * vis[x][y] >= 0)	continue;
				else {
					int tmp = vis[p.x][p.y] + (peo == 1 ? -1 : 1);
					return max_(t , vis[x][y] > 0 ? vis[x][y] : -(vis[x][y] % 3 == 0 ? vis[x][y] / 3 : vis[x][y] / 3 - 1));
				}
			}
			vis[x][y] = vis[p.x][p.y] + (peo == 1 ? -1 : 1);
			q[peo].push(x , y);
		}
	}
	return -1;//暂时无法到达
}
int getans() {//搜索控制函数
	q[1].push(man);
	q[2].push(gir);
	int tmp;
	while(!empty_(q[1]) || !empty_(q[2])) {
		int size = siz(q[1]);
		//男生走3步
		size = siz(q[1]);
		for(int i = 1 ; i <= size ; i++)//这里需要保证男生女生两个队列中所有状态的时间同步,因此有这个循环
			if(!empty_(q[1]))
				if((tmp = bfs(1)) != -1)	return tmp ;
        
		size = siz(q[1]);
		for(int i = 1 ; i <= size ; i++)//同理
			if(!empty_(q[1]))
				if((tmp = bfs(1)) != -1)	return tmp ;
        
		size = siz(q[1]);
		for(int i = 1 ; i <= size ; i++)//
			if(!empty_(q[1]))
				if((tmp = bfs(1)) != -1)	return tmp ;
		//女生走一步
		size = siz(q[2]);
		for(int i = 1 ; i <= size ; i++)
			if(!empty_(q[2]))
				if((tmp = bfs(2)) != -1)	return tmp ;
	}
	return -1;
}
int main() {
	int T = read();
	while(T--) {
		q[1].clear();//记得初始化
		q[2].clear();
		memset(vis , 0 , sizeof(vis));
		memset(map , 0 , sizeof(map));
		
		n = read() , m = read();
		bool gz1 = false;
		for(int i = 1 ; i <= n ; i++) //读入
			for(int j = 1 ; j <= m ; j++){
				char tmp = getchar();
				while(tmp != 'X' && tmp != '.' && tmp != 'M' && tmp != 'G' && tmp != 'Z')
					tmp = getchar();
				map[i][j] = tmp;
				
				if(tmp == 'M')	getp(man);
				if(tmp == 'G')	getp(gir);
				if(tmp == 'Z') {
					if(gz1)
						getp(z2);
					else {
						getp(z1);
						gz1 = true;
					}
				}
			}
		cout << getans() << endl;
	}

	return 0;
}
posted @ 2021-03-27 08:27  追梦人1024  阅读(61)  评论(0编辑  收藏  举报