01 bfs 学习

01 bfs简介

b f s bfs bfs 可以 O ( V + E ) O(V+E) O(V+E) 求解边权全为 1 1 1 的图上最短路。
d i s [ x ] [ y ] dis[x][y] dis[x][y] 求的是起点到 ( x , y ) (x,y) (x,y) 的最短路

而当边权只有 0 0 0 1 1 1 时,使用其它最短路算法是有些浪费的,此时可以使用 b f s bfs bfs 的变种: 01 b f s 01 bfs 01bfs 来快速求解,复杂度仍为 O ( V + E ) O(V+E) O(V+E).

01 b f s 01bfs 01bfs 维护一个双端队列,当边权为 0 0 0 时,使用 p u s h _ f r o n t push\_front push_front,当边权为 1 1 1 时,使用 p u s h _ b a c k push\_back push_back.
节点的出队顺序是这样的: 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 2 , 2 , 2 , 3 , 3 , 3 , … 0,0,0,0,0,1,1,1,1,2,2,2,3,3,3,… 0,0,0,0,0,1,1,1,1,2,2,2,3,3,3,

01 b f s 01bfs 01bfs 常见于迷宫问题,此外有进阶的 ( 1 , 0 ) , ( 1 , 1 ) (1,0),(1,1) (1,0),(1,1) 版本

E - Stronger Takahashi
题意:
给定一个 n × m n\times m n×m 的地图, ‘ # ’ ‘\#’ # 代表墙壁,无法通过, ′ . ′ '.' . 是空地,每次可以花费把一个 2 × 2 2\times 2 2×2 的矩阵变成全是空地的矩阵,无论之前状态如何,起点左上角,终点右下角,求最小花费。
思路:
经典 01 01 01 b f s bfs bfs
code:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 5e2 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
char mp[maxn][maxn];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};

ll dis[maxn][maxn];// 最短路数组 

struct node{

	int x, y;
};
deque <node> q;
bool check(int x, int y){
	if(x < 1 || y < 1 || x > n || y > m) return 1;
	return 0;
}
void bfs()
{
	memset(dis, 0x3f, sizeof(dis));
	dis[1][1] = 0;
	q.push_back({1, 1});
	while(!q.empty())
	{
		node now = q.front();q.pop_front();// 每次取队首元素
		// 边权为0 
		for(int i = 0; i < 4; ++i)
		{
			int xx = now.x + dx[i], yy = now.y + dy[i];
			if(check(xx, yy)) continue;
			if(mp[xx][yy] == '.')
			{
				if(dis[xx][yy] > dis[now.x][now.y])
				{
					dis[xx][yy] = dis[now.x][now.y];
					q.push_front({xx, yy});
				}
			}
		}
		// 边权为1 
		for(int i = -2; i <= 2; ++i)
			for(int j = -2; j <= 2; ++j)
			{
				if(abs(i) == abs(j) && abs(i) == 2) continue;// 不能走对角线,只能摧毁和它相邻的格子产生的 2*2 的正方形
				int xx = now.x + i, yy = now.y + j;
				if(check(xx, yy)) continue;
				if(dis[xx][yy] > dis[now.x][now.y] + 1)
				{
					dis[xx][yy] = dis[now.x][now.y] + 1;
					q.push_back({xx, yy});
				}
			}
	}
}
void work()
{
	cin >> n >> m;
	for(int i = 1; i <= n; ++i) cin >> (mp[i] + 1);
	bfs();
	cout << dis[n][m];
}

int main()
{
//	ios::sync_with_stdio(0);
//	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

ABC176D “Wizard in Maze”
上题修改一下即可

类似题目

uva11573 Ocean Currents
方向与花费有关系,设置好方向数组与题目对于的方向即可

Codeforces 590C Three States
题意:
有三个国家要建立联盟,每个数字表示一个国家,一共 3 3 3 个, ‘ . ’ ‘.’ . 表示可以修路, ′ # ′ '\#' # 表示不可以修路,问最少要多少要建立多少条路使得三个国家的所有城市连通
思路:
这个题要注意,一个国家肯定是连通的,因此同一个国家内的任意两个城市之间花费为 0 0 0
三个国家连通肯定相交于某个点,因此求得每个国家到每个地方的最小花费,然后枚举三个国家都连接到的某个点,维护最小花费。
对于 ‘ . ’ ‘.’ . 的位置要 − 2 -2 2 ,因为这个点只需要被修一次而不是三次,还有就是三个 0 x 3 f 3 f 3 f 3 f 0x3f3f3f3f 0x3f3f3f3f 加起来回超 i n t int int 需要判掉防止溢出
code:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 1e3 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
char mp[maxn][maxn];
ll dis[3][maxn][maxn];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
struct node{
	int x, y;
};

bool check(int x, int y){
	if(x < 1 || y < 1 || x > n || y > m) return 0;
	return 1;
}
void bfs(int id)
{
	deque <node> q;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			if(mp[i][j] - '1' == id){
				dis[id][i][j] = 0;
				q.push_front({i, j});
			}
	while(!q.empty())
	{
		node now = q.front();q.pop_front();
		for(int i = 0; i < 4; ++i)
		{
			int xx = now.x + dx[i], yy = now.y + dy[i];
			int w = (mp[xx][yy] == '.');// 花费 
			if(check(xx, yy) && mp[xx][yy] != '#')
			{
				if(dis[id][xx][yy] > dis[id][now.x][now.y] + w)
				{
					dis[id][xx][yy] = dis[id][now.x][now.y] + w;
					if(w)	q.push_back({xx, yy});
					else q.push_front({xx, yy});
				}
			}
		}
	}
}
void work()
{
	cin >> n >> m;
	for(int i = 1; i <= n; ++i)	cin >> (mp[i] + 1);
	memset(dis, 0x3f, sizeof(dis));
	for(int i = 0; i < 3; ++i) bfs(i);
	ll ans = INF;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
		{
			if(dis[0][i][j] == INF || dis[1][i][j] == INF || dis[2][i][j] == INF) continue;
			// 防止溢出
			ll sum = 0;
			for(int k = 0; k < 3; ++k) sum += dis[k][i][j];
			if(mp[i][j] == '.') sum -= 2;
			ans = min(ans, sum);
		}
	cout << (ans == INF ? -1 : ans);
}

int main()
{
	ios::sync_with_stdio(0);
//	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

GYM100625(BAPC2013) J Jailbreak
题意:
有两个犯人要逃出监狱,监狱中有很多门,打开一道门需要花费 1 1 1 的体力,当一个犯人打开门后另一个就不需要在打开了。走到地图外就算逃出监狱了。 ‘ ∗ ’ ‘*’ 表示墙, ‘ . ’ ‘.’ . 表示路(不花体力), ‘ # ’ ‘\#’ # 表示门(花费 1 1 1 体力),问两个人都逃出去的最小花费,保证有解。
思路:
题解

Codeforces 1063B Labyrinth
题意:
给定 n ∗ m n*m nm 的迷宫和初始坐标 ( r , c ) (r,c) (r,c) ′ . ′ '.' . 表示空地, ′ ∗ ′ '*' 表示障碍,规定向左向右走的步数有限,问能够走到的点有多少个。
思路:
题解1 + 题解2
考虑到两个方向都有限制,比较难写,但是假如只给了一个限制,也就是向左走的步数有限制,那么向左走的代价为 1 1 1,向其他方向走的代价就是 0 0 0,那么变成了 01 01 01 最短路。
这时候可以设 d i s [ i ] [ j ] dis[i][j] dis[i][j] 表示达到 ( i , j ) (i,j) (i,j) 这个点最少需要向左走的步数,当前纵坐标为 j j j,向右走了 r r r 步,则 j = s t y + r − d i s [ i ] [ j ] j=sty+r-dis[i][j] j=sty+rdis[i][j],那么向右走的步数就为 j − s t y + d i s [ i ] [ j ] j-sty+dis[i][j] jsty+dis[i][j] (其中 s t y sty sty 表示起点的纵坐标),只要小于限制就可以到达这个点
code:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 2e3 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
char mp[maxn][maxn];
ll dis[maxn][maxn];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int cost[4] = {0, 1, 0, 0};// 向左走的cost置为1
struct node{
	int x, y;
};
int stx, sty, l, r;

bool check(int x, int y){
	if(x < 1 || y < 1 || x > n || y > m) return 0;
	return 1;
}
void bfs()
{
	deque <node> q;
	memset(dis, 0x3f, sizeof(dis));
	dis[stx][sty] = 0;
	q.push_front({stx, sty});
	while(!q.empty())
	{
		node now = q.front();q.pop_front();
		for(int i = 0; i < 4; ++i)
		{
			int xx = now.x + dx[i], yy = now.y + dy[i];
			int w = cost[i];
			if(check(xx, yy) && mp[xx][yy] != '*')
			{
				if(dis[xx][yy] > dis[now.x][now.y] + w)
				{
					dis[xx][yy] = dis[now.x][now.y] + w;
					if(w)	q.push_back({xx, yy});
					else q.push_front({xx, yy});
				}
			}
		}
	}
}
void work()
{
	cin >> n >> m >> stx >> sty;
	cin >> l >> r;
	for(int i = 1; i <= n; ++i)	cin >> (mp[i] + 1);
	bfs();
	int ans = 0;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j) if(dis[i][j] <= l)
		{
			int d = j - (sty - dis[i][j]);
			if(d <= r) ++ans;
		}
	cout << ans;
}
/*
3 7
3 3
6 0
.......
.******
.......
*/
int main()
{
	ios::sync_with_stdio(0);
//	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

迷宫2
思路:
01bfs+路径回溯
code:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 1e3 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
char mp[maxn][maxn];
ll dis[maxn][maxn];
map <char, int> ma;
int dx[4] = {0, 0, -1, 1};
int dy[4] = {-1, 1, 0, 0};
struct node{
	int x, y;
}pre[maxn][maxn];

bool check(int x, int y){
	if(x < 1 || y < 1 || x > n || y > m) return 1;
	return 0;
}

void bfs()
{
	deque <node> q;
	q.push_back({1, 1});
	dis[1][1] = 0;
	while(!q.empty()){
		node now = q.front();q.pop_front();
		for(int i = 0; i < 4; ++i){
			int xx = now.x + dx[i], yy = now.y + dy[i];
			if(check(xx, yy)) continue;
			int w = (i != ma[mp[now.x][now.y]]);
			if(dis[xx][yy] > dis[now.x][now.y] + w){
				dis[xx][yy] = dis[now.x][now.y] + w;
				if(w) q.push_back({xx, yy});
				else q.push_front({xx, yy});
				pre[xx][yy].x = now.x, pre[xx][yy].y = now.y;
			}
		}
	}	
}
char f(int i){
	if(i == 0) return 'L';
	else if(i == 1) return 'R';
	else if(i == 2) return 'U';
	else return 'D';
}
void work()
{
	cin >> n >> m;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j) dis[i][j] = INF, pre[i][j].x = pre[i][j].y = 0;
	for(int i = 1; i <= n; ++i) cin >> (mp[i] + 1);
	bfs();
	cout << dis[n][m] << endl;
	int x = n, y = m;
	while(x != 1 || y != 1)
	{
		int xx = pre[x][y].x, yy = pre[x][y].y;
		// (xx,yy) ----> (x,y)
		int i = 0;
		while(i < 4){
			if(xx + dx[i] == x && yy + dy[i] == y) break;
			++i;
		}
		int d = ma[mp[xx][yy]];
		if(d != i){
			cout << xx <<  " " << yy << " " << f(i) << endl;
		}
		x = xx, y = yy;
	}
}

int main()
{
	ios::sync_with_stdio(0);
	ma['L'] = 0, ma['R'] = 1, ma['U'] = 2, ma['D'] = 3;
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

拓展

除了求边权为0与1的最短路之外,还可以求边权为(1,1)与(1,0)的最短路,二维费用。第一要求是第一维费用最小,第二要求是第二维费用最小。
写法是更新第一维时,插入到队尾。不更新第一维但更新第二维时,插入到队首。

LA6011 Error
Codeforces 1065D. Three Pieces
题解链接

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值