ABC213 E - Stronger Takahashi 题解
兼 01-BFS 学习笔记
题面
有一个被分为H行W列格子。有的格子可以通行,有的格子有岩石不能通行,不能超过格子。
现在Takahashi在移动过程中,可以一拳破坏2*2的格子中的岩石。
给定地图,请问至少出多少拳破坏岩石,能让Takahashi从左上角走到右下角。
\(H,W\leq 500\)
解题
涨知识了,第一次碰到用 \(01-\text{BFS}\) 做的题目。
本来想到是如何“手动联编” 乱搞建图,然后直接上 \(dijstra\)
瞎 jb 乱搞过后不知道怎么出问题(思路应该是没有问题的),直接就打 \(\text{DFS}\) 暴力了,走人,开 F 题。
那么,什么是 \(01-\text{BFS}\) 呢,
我们知道,\(\text{BFS}\) 可以 \(O(V+E)\) 求解边权全为 \(1\) 的图上最短路
而当边权只有 \(0\) 或 \(1\) 时,没必要使用其它最短路算法(并且可能处理方式复杂),此时可以使用 \(01-\text{BFS}\) 来求解,复杂度仍为 \(O(V+E)\).
具体的,\(01-\text{BFS}\) 维护一个双端队列,当边权为 \(0\) 时, \(push\_front\),当边权为 \(1\) 时,\(push\_back\)
正确性?这可以理解为一种贪心的思路,先拓展花费较小的点(有点像 \(SPFA\) 的 \(SLF\) 优化)
重要的是,它同时有 \(bfs\) 的特性,也就是第一次拓展到的就是最小代价。
这样说,\(01-\text{BFS}\) 可以拓展于代价为 \(0\) 和 \(1\) 的两种抉择求代价最少的问题中
代码
应该是可以轻松理解的。
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N = 505;
string s[N];
pii zero[]={{0,-1},{0,1},{-1,0},{1,0}};
pii one[]={{0,-1},{0,1},{-1,0},{1,0},
{-1,-1},{-1,1},{1,-1},{1,1},
{-2,-1},{-2,0},{-2,1},{2,-1},
{2,0},{2,1},{-1,-2},{0,-2},
{1,-2},{-1,2},{0,2},{1,2}
};
int h,w;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>h>>w;
for(int i=0;i<h;++i)
cin>>s[i];
vector<int>dis(h*w,-1);
deque<pii >q;
q.emplace_back(0,0);
while(q.size()){
auto [u,d]=q.front();q.pop_front();
if(dis[u]!=-1)continue;
dis[u]=d;
for(auto[dx,dy]:zero){
int x=u/w+dx,y=u%w+dy;
if(0<=x&&x<h&&0<=y&&y<w&&s[x][y]=='.')
q.emplace_front(x*w+y,d);
}
for(auto[dx,dy]:one){
int x=u/w+dx,y=u%w+dy;
if(0<=x&&x<h&&0<=y&&y<w)
q.emplace_back(x*w+y,d+1);
}
}
cout<<dis[h*w-1]<<"\n";
return 0;
}