YbtOJ 「基础算法」 第5章 广度搜索

广搜

A. 【例题1】走迷宫图

[题目描述]

现在有一个nn的地图,问从起点(sx,sy)到终点(tx,ty)最少要走几步。

[输入格式]

第一行一个正整数n

接下来n行,每行n个字符,表示nn01矩阵,1表示不能通过,0表示可以通过。

最后一行四个整数sx,sy,tx,ty

[输出格式]

仅有一个数,表示答案。

[算法分析]

广搜裸题 注意check的时候要判断障碍

[代码实现]

#include <bits/stdc++.h>
using namespace std;
#define inl inline
const int N = 1e3 + 5;
const int dx[5] = { 0 , 0 , 0 , 1 , -1 };
const int dy[5] = { 0 , 1 , -1 , 0 , 0 };

inl int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
	return x * f;
}

int n , sx , sy , tx , ty , vis[N][N] , dep[N][N];
struct node { int x , y; };
char ch;
int check ( int x , int y ) { return 1 <= x && x <= n && 1 <= y && y <= n && !vis[x][y]; }

void bfs()
{
	queue <node> q;
	q.push ( (node) { sx , sy } );
	dep[sx][sy] = 0 , vis[sx][sy] = 1;
	while ( !q.empty() )
	{
		int x = q.front().x , y = q.front().y; q.pop();
		if ( x == tx && y == ty ) { printf ( "%d" , dep[x][y] ); return; }
		for ( int i = 1 ; i <= 4 ; i ++ )
		{
			int xx = x + dx[i] , yy = y + dy[i];
			if ( !check ( xx , yy ) ) continue; 
			dep[xx][yy] = dep[x][y] + 1;
			vis[xx][yy] = 1;
			q.push ( (node) { xx , yy } );
		}
	}
}

signed main ()
{
	n = read();
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= n ; j ++ )
			cin >> ch , vis[i][j] = ch - '0';
	sx = read() , sy = read() , tx = read() , ty = read();
	bfs();
	return 0;
}

B. 【例题2】山峰山谷

[题目描述]

给定一个nn的网格状地图,每个方格(i,j)有一个高度wi,j 如果两个方格有公共顶点,则它们是相邻的。

定义山峰山谷如下:

均由地图上的一个联通块组成。
所有方格高度都相同。
周围的方格(即不属于山峰或山谷但与山峰或山谷相邻的格子)高度均大于山谷的高度,或小于山峰的高度。
求地图内山峰和山谷的数量。特别的,如果整个地图方格的高度均相同,则整个地图即是一个山谷,也是一个山峰.

[输入格式]

第一行一个正整数n,表示地图的大小。

接下来n行,每行n整数表示地图。第i行有n个正整数,表示地图第i行格子的高度。

[输出格式]

输出一行两个整数,分别表示山峰和山谷的数量。

[算法分析]

对于每一个联通块进行一次广搜并标记

如果搜到了边界 如果这个边界值大于联通块值 那么这个联通块不可能是山峰 山谷也同理

最后输出山峰联通块个数和山谷联通块个数即可

[代码实现]

#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define fi first
#define se second
#define pii pair<int,int>
const int N = 1e3 + 5;
const int dx[9] = { 0 , 0 , 0 , 1 , -1 , 1 , 1 , -1 , -1 };
const int dy[9] = { 0 , 1 , -1 , 0 , 0 , -1 , 1 , -1 , 1 };

inl int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
	return x * f;
}
int n , sx , sy , tx , ty , w[N][N] , vis[N][N] , not_up , not_down , cnt_up , cnt_down;
inl int check ( int x , int y ) { return 1 <= x && x <= n && 1 <= y && y <= n; }
void bfs ( int sxx , int syy , int val )
{
	not_up = 0 , not_down = 0;
	queue < pii > q;
	q.push ( make_pair ( sxx , syy ) );
	vis[sxx][syy] = 1;
	while ( !q.empty() )
	{
		int x = q.front().fi , y = q.front().se; q.pop();
		for ( int i = 1 ; i <= 8 ; i ++ )
		{
			int xx = x + dx[i] , yy = y + dy[i];
			if ( !check ( xx , yy ) ) continue; 
			if ( !vis[xx][yy] && val == w[xx][yy] )
			{
				vis[xx][yy] = 1;
				q.push ( make_pair(xx,yy) );
			}
			if ( w[xx][yy] > val ) not_up = 1;
			if ( w[xx][yy] < val ) not_down = 1;
		}
	}
	if ( !not_up ) cnt_up ++;
	if ( !not_down ) cnt_down ++;
}

signed main ()
{
	n = read();
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= n ; j ++ )
			cin >> w[i][j];
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= n ; j ++ )
			if ( !vis[i][j] ) bfs(i,j,w[i][j]);
	printf ( "%d %d" , cnt_up , cnt_down );
	return 0;
}

C. 【例题3】荆轲刺秦

[题目描述]

时隔数年,刺客荆轲再次来到咸阳宫,试图刺杀嬴政。

咸阳宫的地图可以描述为一个 nm 列的矩形。在这里,我们规定每一行中从左到右为 x 轴正方向,每一列中从下到上为 y 轴正方向,左下角的点坐标为 (1,1)。矩形中的点可以分为 4 种:

  1. 起点,也就是荆轲的所在点,在地图中用字符 S 代表。
  2. 终点,也就是嬴政的所在点,在地图中用字符 T 代表。
  3. 卫兵,在地图中用一个正整数 ai,j 代表。在这里,一个卫兵 (i,j) 可以观察到与他曼哈顿距离小于 ai,j 的点。也就是卫兵 (i,j) 可以观察到所有满足 |xi|+|yj|<ai,j 的点 (x,y)
  4. 空地,在地图中用字符 . 代表。

荆轲的正常移动方式为每秒向八连通的任意方向前进一格。如下图,中间的点为荆轲当前所在点,每一秒,他可以走向其余的八个点。

img

需要注意的是,正常移动时,荆轲不能踏进任何一个有卫兵或者卫兵能观察到的格子。当然,他也不能走出咸阳宫,也就是说,无论何时,荆轲的坐标 (x,y) 都必须满足 1xm1yn

荆轲还有两种技能:隐身和瞬移。

  1. 隐身:下一秒荆轲进入隐身状态,卫兵观察不到荆轲,荆轲可以进入卫兵的观察范围内,但仍然不能进入卫兵所在的格子。注意这个状态只能维持一秒。
  2. 瞬移:荆轲下一秒移动的距离改为 d,但这时只能向上下左右四个方向移动。即可以移动到
    (x+d,y)(xd,y)(x,y+d)(x,yd)
    在本题中,两种技能可以同时使用,而且不考虑冷却时间,即一次用完可以立即用下一次,两种技能都分别有使用次数限制,你也可以不用完所有次数。

现在给出咸阳城的地图,请计算荆轲到达秦王所在点所需的最短时间。此外,在所用时间相同情况下,荆轲希望使用的两种技能总次数尽可能少;在所用时间与技能次数相同情况下,荆轲希望使用的隐身次数尽可能少。

[输入格式]

第一行五个整数 n, m, c1, c2, d,代表地图的大小为 n×m,隐身的使用限制次数为 c1,瞬移的使用限制次数为 c2 和一次瞬移的距离为 d

接下来 n 行,每行 m 个元素。每个元素为字符 ST. 或者一个正整数 ai,j,代表一个格点,具体含义详见题目描述。

[输出格式]

若荆轲无法到达秦王所在点,则输出一行一个 1

否则输出一行三个整数 t, u1, u2,依次代表所需的最短时间,隐身的使用次数与瞬移的使用次数。

[算法分析]

首先考虑如何处理士兵能观察到的范围(例:k=3)

img

那么考虑差分 对于每一行来说 菱形的最左点位置加一 菱形的最右点+1的位置减一

每次bfs中四种情况:

  1. 不隐形不瞬移
  2. 隐形不瞬移
  3. 瞬移不隐形
  4. 隐形又瞬移

设一个四元组(x,y,yx,sy)表示这一个状态

[代码实现]

#include <bits/stdc++.h>
#define inl inline
using namespace std;
const int N = 355;
const int inf = 233333333;
const int dx[9] = { 0 , 1 , 0 , -1 , 0 , 1 , -1 , -1 , 1 };
const int dy[9] = { 0 , 0 , 1 , 0 , -1 , 1 , 1 , -1 , -1 };
inl int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
	return x * f;
}
int c1 , c2 , n , m , d , mp[N][N] , tag[N][N];
int sx , sy , tx , ty;
bool vis[N][N][20][20],look[N][N];//vis是走没走过(xy坐标,隐形,瞬移),look是能不能看到 
string s;
struct qwq
{
	int x , y , yx , sy , s;
	//x,y——对应的坐标
	//s——走到这一步的步数
	//yx——隐形使用次数
	//sy——瞬移使用次数
}ans;

void lookaround ( int x , int y , int k )
{
	for ( int i = 0 ; i <= k ; i ++ )
	{
		tag[min(x+i,n)][max(y-(k-i),1)] ++;
		tag[min(x+i,n)][min(y+(k-i),m)+1] --;
		tag[max(x-i,1)][max(y-(k-i),1)] ++;
		tag[max(x-i,1)][min(y+(k-i),m)+1] --;
	}//(k-i)是另一个坐标的曼哈顿步数
}

qwq minn ( qwq x , qwq y )
{
	if ( x.s != y.s ) return x.s < y.s ? x : y;
	if ( x.yx + x.sy != y.yx + y.sy ) return x.yx + x.sy < y.yx + y.sy ? x : y;
	return x.yx < y.yx ? x : y;
}

int check ( int x , int y ) { return 1 <= x && x <= n && 1 <= y && y <= m && mp[x][y] <= 0; }
queue <qwq> q;

void bfs()
{
	q.push ( (qwq) { sx , sy , 0 , 0 , 0 } );
	vis[sx][sy][0][0] = 1;
	while ( !q.empty() )
	{
		qwq tmp = q.front(); q.pop();
		if ( tmp.s > ans.s ) continue;
		if ( tmp.x == tx && tmp.y == ty )
		{ ans = minn ( ans , tmp ); continue; }
		for ( int i = 1 ; i <= 8 ; i ++ )//瞬移
		{
			int xx = tmp.x + dx[i] , yy = tmp.y + dy[i];
			if ( !check ( xx , yy ) ) continue;
			if ( look[xx][yy] )
			{
				if ( vis[xx][yy][tmp.yx+1][tmp.sy] || tmp.yx + 1 > c1 ) continue;
				vis[xx][yy][tmp.yx+1][tmp.sy] = 1;
				q.push ( (qwq) { xx , yy , tmp.yx + 1 , tmp.sy , tmp.s + 1 } );
			}
			else
			{
				if ( vis[xx][yy][tmp.yx][tmp.sy] ) continue;
				vis[xx][yy][tmp.yx][tmp.sy] = 1;
				q.push ( (qwq) { xx , yy , tmp.yx , tmp.sy , tmp.s + 1 } );
			}
		}
		if ( tmp.sy + 1 > c2 ) continue;//瞬移用不了了
		for ( int i = 1 ; i <= 4 ; i ++ )//不瞬移
		{
			int xx = tmp.x + dx[i] * d , yy = tmp.y + dy[i] * d;
			if ( !check ( xx , yy ) ) continue;
			if ( look[xx][yy] )
			{
				if ( vis[xx][yy][tmp.yx+1][tmp.sy+1] || tmp.yx + 1 > c1 ) continue;
				vis[xx][yy][tmp.yx+1][tmp.sy+1] = 1;
				q.push ( (qwq) { xx , yy , tmp.yx + 1 , tmp.sy + 1 , tmp.s + 1 } );
			}
			else
			{
				if ( vis[xx][yy][tmp.yx][tmp.sy+1] ) continue;
				vis[xx][yy][tmp.yx][tmp.sy+1] = 1;
				q.push ( (qwq) { xx , yy , tmp.yx , tmp.sy + 1 , tmp.s + 1 } );
			}
		}
	}
}



int main ()
{
	scanf("%d%d%d%d%d", &n, &m, &c1, &c2, &d);
	ans = { 0 , 0 , inf , inf , inf };
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= m ; j ++ )
	{
		cin >> s;
		if ( s[0] == 'S' ) sx = i , sy = j , mp[i][j] = -1;
		else if ( s[0] == 'T' ) tx = i , ty = j , mp[i][j] = -2;
		else if ( s[0] == '.' ) mp[i][j] = 0;
		else 
		{
			int x = 0;
			for ( int p = 0 ; p < s.size() ; p ++ ) x = x * 10 + ( s[p] - '0' );
			mp[i][j] = x;
			lookaround ( i , j , x - 1 );
		}
	}
	for ( int i = 1 ; i <= n ; i ++ )
	{
		int sum = 0;
		for ( int j = 1 ; j <= m ; j ++ )
		{
			sum += tag[i][j];
			if ( sum > 0 ) look[i][j] = 1;
		}
	}
	bfs();
	if ( ans.s == inf ) printf ( "-1" );
	else printf ( "%d %d %d" , ans.s , ans.yx , ans.sy );
	return 0;
} 

D. 【例题4】电路维修

[题目描述]

有一种正方形的电路原件,在它的两组相对顶点中,有一组会用导线连接起来,另一组则不会。

有 这样的原件,你想将其排成nm列放在电路板上。电路板的左上角连接电源,右下角连接灯泡。

img

试求:至少要旋转多少个正方形元件才能让电源与灯泡联通,若无解则输出 NO SOLUTION

[输入格式]

有多组测试数据。

一行为测试数据组数,以下每组测试数据描述为:

一行有两个整数nm

接下来的n行中,每行有m个字符。每个字符均为 \/,表示正方形元件上导线的连接方向。

[输出格式]

每组测试数据输出描述:

输出共一行,若有解则输出一个整数,表示至少要旋转多少个正方形元件才能让电源与灯泡联通;若无解则输出 NO SOLUTION

[算法分析]

数组大小!!!!!!!wssb

考虑dijkstra最短路 将每一个题中给定的的路径边权设置为0 将每一个需要旋转才能到达的边设置为1 建立双向边 那么跑最短路即可

[代码实现]

#include <bits/stdc++.h>
#define inl inline
#define fi first
#define se second
#define mkp make_pair
#define pii pair<int,int>
using namespace std;
const int M = 500 + 5;
const int N = M * M + 5;
const int inf = 0x3f3f3f3f;
inl int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
	return x * f;
}
int n , m , tot , mark[M][M];
char s[M][M];

struct node { int to , nxt , w; } e[N<<4];
int head[N] , cnt;
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w } , head[u] = cnt; }

int dis[N];
priority_queue < pii , vector<pii> , greater<pii> > q;
void dij ( int s )
{
	for ( int i = 1 ; i <= (m+1) * (n+1); i ++ ) dis[i] = inf;
	dis[s] = 0;
	q.push ( mkp ( 0 , s ) );
	while ( !q.empty() )
	{
		int u = q.top().se , f = q.top().fi; q.pop();
		if ( f != dis[u] ) continue;
		for ( int i = head[u] ; i ; i = e[i].nxt )
		{
			int v = e[i].to;
			if ( dis[v] > dis[u] + e[i].w )
			{
				dis[v] = dis[u] + e[i].w;
				q.push ( mkp(dis[v],v) );
			}
		}
	}
}

void init()
{
	memset ( head , 0 , sizeof ( head ) );
	cnt = tot = 0;
}

signed main ()
{
	int T = read();
	while ( T -- )
	{
		init();
		n = read() , m = read();
		for ( int i = 1 ; i <= n + 1 ; i ++ )
			for ( int j = 1 ; j <= m + 1 ; j ++ )
				mark[i][j] = ++tot;
		for ( int i = 1 ; i <= n ; i ++ )
			for ( int j = 1 ; j <= m ; j ++ )
			{
				cin >> s[i][j];
				if ( s[i][j] == '/' )
				{
					add ( mark[i][j] , mark[i+1][j+1] , 1 );
					add ( mark[i+1][j+1] , mark[i][j] , 1 );
					add ( mark[i+1][j] , mark[i][j+1] , 0 );
					add ( mark[i][j+1] , mark[i+1][j] , 0 );
				}
				else 
				{
					add ( mark[i][j] , mark[i+1][j+1] , 0 );
					add ( mark[i+1][j+1] , mark[i][j] , 0 );
					add ( mark[i+1][j] , mark[i][j+1] , 1 );
					add ( mark[i][j+1] , mark[i+1][j] , 1 );
				}
			}
		dij ( mark[1][1] );
		if ( dis[mark[n+1][m+1]] == inf ) printf ( "NO SOLUTION\n" );
		else printf ( "%d\n" , dis[mark[n+1][m+1]] );
	}
	return 0;
}

E. 【例题5】逃离噩梦

[题目描述]

给定一张NM的地图,地图中有1个男孩,1个女孩和2个鬼。

字符 . 表示道路,字符 X 表示墙,字符 M 表示男孩的位置,字符 G 表示女孩的位置,字符 Z 表示鬼的位置。

男孩每秒可以移动3个单位距离,女孩每秒可以移动1个单位距离,男孩和女孩只能朝上下左右四个方向移动。

每个鬼占据的区域每秒可以向四周扩张2个单位距离,并且无视墙的阻挡,也就是在第k秒后所有与鬼的曼哈顿距离不超过2k的位置都会被鬼占领。

注意:每一秒鬼会先扩展,扩展完毕后男孩和女孩才可以移动。

求在不进入鬼的占领区的前提下,男孩和女孩能否回合,若能回合,求出最短会和时间。

[输入格式]

第一行包含整数T,表示共有T组测试用例。

每组测试用例第一行包含两个整数NM,表示地图的尺寸。

接下来N行每行M个字符,用来描绘整张地图的状况。

注意:地图中一定有且仅有1个男孩,1个女孩和2个鬼。

[输出格式]

每个测试用例输出一个整数S,表示最短会合时间。

如果无法会合则输出 -1

每个结果占一行。

[算法分析]

每一次枚举男孩和女孩 更新此时队列中男女的所有点(这时候的队列中男女所有点就是这一个时刻 男女分别能到达什么地方 将扩展后的路径记录)

一旦男/女走到了路径重合处 那么返回time

要看清check的是xx还是t.x 并要注意清零cnt

[代码实现]

#include <bits/stdc++.h>
using namespace std;
#define inl inline
const int N = 810;
const int dx[5] = { 0 , 0 , 0 , 1 , -1 };
const int dy[5] = { 0 , 1 , -1 , 0 , 0 };
inl int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
	return x * f;
}


struct point {
    int x, y;
}b,g,gui[3];
int n , m , cnt , mp[N][N];
char ch;

bool check ( int x , int y , int tim )
{
	if ( x <= 0 || y <= 0 || x > n || y > m || mp[x][y] == -1 ) return false; 
	for ( int i = 1 ; i <= 2 ; i ++ ) if ( abs ( x - gui[i].x ) + abs ( y - gui[i].y ) <= tim * 2 ) return false;
	return true;
}



int bfs()
{
	queue < point > boy , girl;
	boy.push(b) , girl.push(g);
	for ( int timer = 1 ; !boy.empty() || !girl.empty() ; timer ++ )
	{
		for ( int stp = 1 ; stp <= 3 ; stp ++ )//三步 每一步把上一步能走到的点拿出来处理
		{
			for ( int j = 1 , len = boy.size() ; j <= len ; j ++ )
			{
				point t = boy.front(); boy.pop();
				if ( !check ( t.x , t.y , timer ) ) continue;
				for ( int k = 1 ; k <= 4 ; k ++ )
				{
					int xx = t.x + dx[k] , yy = t.y + dy[k];
					if ( !check ( xx , yy , timer ) ) continue;
					if ( mp[xx][yy] == 1 ) return timer;
					if ( mp[xx][yy] == 0 ) { mp[xx][yy] = 2; boy.push ( (point) { xx , yy } ); }
				}
			}
		}
		//一步即可
		for ( int j = 1 , len = girl.size() ; j <= len ; j ++ )
		{
			point t = girl.front(); girl.pop();
			if ( !check ( t.x , t.y , timer ) ) continue;
			for ( int k = 1 ; k <= 4 ; k ++ )
			{
				int xx = t.x + dx[k] , yy = t.y + dy[k];
				if ( !check ( xx , yy , timer ) ) continue;
				if ( mp[xx][yy] == 2 ) return timer;
				if ( mp[xx][yy] == 0 ) { mp[xx][yy] = 1; girl.push ( (point) { xx , yy } ); }
			}
		}
	}
	return -1;
}


void init()
{
	memset ( mp , 0 , sizeof mp );
	cnt = 0;
}

signed main ()
{
	int T = read();
	while ( T -- )
	{
		init();
		n = read() , m = read();
		for ( int i = 1 ; i <= n ; i ++ )
			for ( int j = 1 ; j <= m ; j ++ )
			{
				cin >> ch;
				if ( ch == 'X' ) mp[i][j] = -1;
				if ( ch == 'Z' ) mp[i][j] = -1 , gui[++cnt] = (point){i,j};
				if ( ch == 'G' ) mp[i][j] = 1 , g = (point) { i , j };//女1男2
				if ( ch == 'M' ) mp[i][j] = 2 , b = (point) { i , j };
			}
		printf ( "%d\n" , bfs() );
	}
	return 0;
}

F. 1.最小权值

考虑二分这个最小权值 二分的下界就是第n行的所有点的权值最大值 上界就是整张图中所有点的权值最大值

每次以二分的最小权值作为界限 如果第一行所有点都无法到达第n行 那么说明最小权值太小了 收缩下界 否则收缩上界

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid ((l+r)>>1)
const int N = 1e3 + 5;
const int inf = 0x3f3f3f3f;
int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , l = -inf , r = -inf , a[N][N] , head = 1 , tail = 0;
int vis[N][N];

int check ( int x , int y , int val ) { return 1 <= x && x <= n && 1 <= y && y <= m && a[x][y] <= val && !vis[x][y]; }

struct node { int x , y; } q[10000000];


int dx[5] = { 0 , 1 , -1 , 0 , 0 };
int dy[5] = { 0 , 0 , 0 , 1 , -1 };

int bfs ( int x )
{
	head = 1 , tail = 0;
	memset ( vis , 0 , sizeof vis );
	for ( int i = 1 ; i <= m ; i ++ ) if ( a[1][i] <= x ) q[++tail] = (node){ 1 , i } , vis[1][i] = 1;
	while ( head <= tail )
	{
		node u = q[head++];
		for ( int i = 1 ; i <= 4 ; i ++ )
		{
			int tx = u.x + dx[i] , ty = u.y + dy[i];
			if ( !check ( tx , ty , x ) ) continue;
			if ( tx == n ) return 1;
			vis[tx][ty] = 1;
			q[++tail] = (node){ tx , ty };
		}
	}
	return 0;	
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) for ( int j = 1 ; j <= m ; j ++ ) a[i][j] = read() , r = max ( r , a[i][j] );
	for ( int i = 1 ; i <= n ; i ++ ) l = max ( l , a[n][i] );
	while ( l <= r )
	{
		if ( bfs(mid) ) r = mid - 1;
		else l = mid + 1;
	}
	cout << l << endl;
	return 0;
}

G. 2.射击问题

每次移动龙就需要重新打标记 显然是不明智的 所以我们在每一组询问开始的时候标记猎物可以被打到的地方 再移动龙到标记点上 记录最小步数

坑点:一定不要将龙和猎物的坐标看反了!!!!!!

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N = 1e3 + 5;
const int dx[] = { 0 , 1 , -1 , 0 , 0 , 1 , 1 , -1 , -1 };
const int dy[] = { 0 , 0 , 0 , 1 , -1 , 1 , -1 , 1 , -1 };
int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , mp[N][N] , stx , sty , edx , edy , vis[N][N] , dep[N][N] , mark[N][N];

struct node { int x , y; } q[10000000];

int check ( int x , int y ) { return 1 <= x && x <= n && 1 <= y && y <= m && !mp[x][y] && !vis[x][y]; }

void mk ()
{
	mark[stx][sty] = 1;
	for ( int i = 1 ; i <= 8 ; i ++ )
	{
		int x = stx , y = sty;
		while ( check ( x + dx[i] , y + dy[i] ) ) x += dx[i] , y += dy[i] , mark[x][y] = 1;
	}
}

int bfs ()
{

	int head = 1 , tail = 0;
	vis[edx][edy] = 1;
	q[++tail] = (node) { edx , edy };
	while ( head <= tail )
	{
		node u = q[head++];
		for ( int i = 1 ; i <= 4 ; i ++ )
		{
			int tx = u.x + dx[i] , ty = u.y + dy[i];
			if ( !check ( tx , ty ) || vis[tx][ty] ) continue;
			dep[tx][ty] = dep[u.x][u.y] + 1 , vis[tx][ty] = 1;
			if ( mark[tx][ty] ) return dep[tx][ty];
			q[++tail] = (node) { tx , ty };
		}
	}
	return -1;
}

char ch;

void init()
{
	memset ( dep , 0 , sizeof dep );
	memset ( vis , 0 , sizeof vis );
	memset ( mark , 0 , sizeof mark );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= m ; j ++ )
		{
			cin >> ch;
			if ( ch == 'X' ) mp[i][j] = 1;
		}
	while ( 1 )
	{
		init();
		cin >> stx >> sty >> edx >> edy;//猎物是st 龙是ed
		if ( !stx && !sty && !edx && !edy ) break;
		mk();
		if ( mark[edx][edy] ) { cout << 0 << endl; continue; }
		int res = bfs();
		if ( res == -1 ) cout << "Impossible!" << endl;
		else cout << res << endl;
	}	
	return 0;
}

H. 3.追捕小狗

每次模拟人先扩展 狗再扩展 如果人的当前步数已经扩展完了 那么判断是否到达狗的位置

狗的转圈可以用取模来实现

麻了 cin90分 scanf100分

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N = 1e3 + 5;
const int dx[] = { -1 , 0 , 1 , 0 };
const int dy[] = { 0 , 1 , 0 , -1 };
int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , mp[N][N] , vis[N][N] , timer , st;

struct node { int x , y; } q[10000000] , p , d;


int check ( int x , int y ) { return 1 <= x && x <= n && 1 <= y && y <= n && !mp[x][y]; }

int bfs ()
{
	int head = 1 , tail = 0;
	q[++tail] = p;
	vis[p.x][p.y] = 1;
	while ( head <= tail )
	{
		int sz = tail - head + 1;
		for ( int i = 1 ; i <= sz ; i ++ )
		{
			node u = q[head++]; 
			int x = u.x , y = u.y;
			for ( int i = 0 ; i < 4 ; i ++ )
			{
				int tx = x + dx[i] , ty = y + dy[i];
				if ( !check ( tx , ty ) || vis[tx][ty] ) continue;
				vis[tx][ty] = 1;
				q[++tail] = (node) { tx , ty };
			}
		}
		if ( vis[d.x][d.y] ) return timer;
		int tx = d.x + dx[st] , ty = d.y + dy[st] , cnt = 0;
		while ( !check ( tx , ty ) )
		{
			st = ( st + 1 ) % 4;
			tx = d.x + dx[st] , ty = d.y + dy[st];
			cnt ++;
			if ( cnt > 3 ) return -1;
		}
		d = (node) { tx , ty };
		++ timer; 
	}
	return -1;
}

char g[N][N];
signed main ()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0) , cout.tie(0);
	n = read();
	for ( int i = 1 ; i <= n ; i ++ ) scanf ( "%s" , g[i] + 1 );
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= n ; j ++ )
		{
//			cin >> g[i][j];//用cin的话会90分????????不李姐
			if ( g[i][j] == '*' ) mp[i][j] = 1; 
			if ( g[i][j] == 'F' ) p.x = i , p.y = j;
			if ( g[i][j] == 'J' ) d.x = i , d.y = j;
		}
	int res = bfs();
	if ( res == -1 ) cout << "No solution." << endl;
	else cout << res << endl;
	return 0;
}

I. 4.渡过河流

我们可以从地图边界开始bfs 每次遇到不同的地形就说明需要一个竹筏来过渡 可以预处理出所有点的答案并批量回答询问

注意: bfs 分层 dfs 填充染色, dfs 的时候顺便将下一层的节点塞进队列中。

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'

const int N = 1e3 + 5;
const int dx[] = { 0 , 1 , -1 , 0 , 0 , 1 , 1 , -1 , -1 };
const int dy[] = { 0 , 0 , 0 , 1 , -1 , 1 , -1 , 1 , -1 };
int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , mp[N][N] , head = 1 , tail = 0 , color , col[N][N];

struct node { int x , y; } q[100000000];


int check ( int x , int y ) { return 0 <= x && x <= n && 0 <= y && y <= n && !col[x][y]; }

void dfs ( int x , int y )
{
	for ( int i = 1 ; i <= 8 ; i ++ )
	{
		int tx = x + dx[i] , ty = y + dy[i];
		if ( !check ( tx , ty ) ) continue;
		if ( mp[tx][ty] == mp[x][y] )
		{
			col[tx][ty] = col[x][y];
			dfs ( tx , ty );
		}
		else 
		{
			col[tx][ty] = col[x][y] + 1;
			q[++tail] = (node) { tx , ty };
		}
	}
}


void bfs ()
{
	head = 1 , tail = 0;
	q[++tail] = (node) { 0 , 0 };
	col[0][0] = 1;
	while ( head <= tail )
	{
		node u = q[head++];
		int x = u.x , y = u.y;
		dfs ( x , y );
	}
}

char ch;
signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) for ( int j = 1 ; j <= n ; j ++ ) cin >> ch , mp[i][j] = ch - '0';
	n += 1;
	bfs();	
	while ( m -- ) 
	{
		int i = read() , j = read();
		cout << ( col[i][j] - 1 ) / 2  << ' ';
	}
	return 0;
}
posted @   Echo_Long  阅读(301)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示