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
n∗m 的迷宫和初始坐标
(
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+r−dis[i][j],那么向右走的步数就为
j
−
s
t
y
+
d
i
s
[
i
]
[
j
]
j-sty+dis[i][j]
j−sty+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
题解链接