原理
使用BFS可以求解最短路径,前提是:所有边的权值均相同。
使用队列完成BFS,在遍历的过程中,每次将队首的元素弹出,然后再将该队首相邻的且未被遍历的数据插入队尾,直到队列为空停止。
BFS可以解决的问题:
(1)Floodfill
:参考网址
(2)最短路问题
(3)多源BFS问题
(4)最小步数问题
(5)双端队列广搜
(6)双向广搜
(7)A*
这里只讲解后六种类型。
BFS求最短路的正确性证明:
我们需要证明任意时刻队列中的数据具有:1. 两段性:最多有两段不同的数据;2. 单调性:队列后面的数据一定大于等于前面的数据
可以使用数学归纳法进行证明:
(1)刚开始队列中只有一个起点,到自己的距离为0,满足上面两条性质;
(2)假设某一时刻满足上面两条性质,那么
因此任意时刻队列都具有两段性和单调性。
证明完上述性质后,我们有两种方法证明BFS是正确的。
方法1:这里的队列相当于dijstkra
算法中的优先队列,因为dijstkra
算法是正确的,所以BFS是正确的。
方法2:使用数学归纳法+反证法证明,归纳假设为:所有已经出队的元素的最小值不会再改变了。
(1)当起点出队后,起点到自己的距离为0,是最小值,归纳的起点成立;
(2)假设某一时刻上述归纳假设成立,那么考虑下一时刻:
因此归纳假设成立,所以BFS是正确的。
问题描述
分析
代码
#include
#include
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 110, M = N * N;
int n, m;
int g[N][N];
PII q[M];
int d[N][N]; // 记录步数的数组,同时还具有判重作用
int bfs() {
memset(d, -1, sizeof d);
int hh = 0, tt = 0;
q[0] = {0, 0};
d[0][0] = 0;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
while (hh <= tt) {
auto t = q[hh++];
for (int i = 0; i < 4; i++) {
int a = t.x + dx[i], b = t.y + dy[i];
if (a >= 0 && a < n && b >= 0 && b < m && d[a][b] == -1 && g[a][b] == 0) {
q[++tt] = {a, b};
d[a][b] = d[t.x][t.y] + 1;
}
}
}
return d[n - 1][m - 1];
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
cin >> g[i][j];
cout << bfs() << endl;
return 0;
}
问题描述
分析
(0, 0)->(n - 1, n - 1)
的路径,我们反向遍历即可,即从(n - 1, n - 1)
遍历到(0, 0)
代码
#include
#include
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010, M = N * N;
int n;
int g[N][N];
PII q[M];
PII pre[N][N]; // 记录路径,同时还有判重的作用
void bfs(int sx, int sy) {
memset(pre, -1, sizeof pre);
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int hh = 0, tt = 0;
q[0] = {sx, sy};
while (hh <= tt) {
auto t = q[hh++];
for (int i = 0; i < 4; i++) {
int a = t.x + dx[i], b = t.y + dy[i];
if (a < 0 || a >= n || b < 0 || b >= n) continue; // 出界
if (g[a][b]) continue; // 障碍物
if (pre[a][b].x != -1) continue; // 已经被遍历过
q[++tt] = {a, b};
pre[a][b] = t;
}
}
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
scanf("%d", &g[i][j]);
bfs(n - 1, n - 1);
PII end(0, 0);
while (true) {
printf("%d %d\n", end.x, end.y);
if (end.x == n - 1 && end.y == n - 1) break;
end = pre[end.x][end.y];
}
return 0;
}
问题描述
分析
代码
#include
#include
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 155, M = N * N;
int n, m;
char g[N][N];
PII q[M];
int dist[N][N];
int bfs() {
int dx[] = {-2, -1, 1, 2, 2, 1, -1, -2};
int dy[] = {1, 2, 2, 1, -1, -2, -2, -1};
// 寻找起点
int sx, sy;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (g[i][j] == 'K') {
sx = i, sy = j;
break;
}
memset(dist, -1, sizeof dist);
int hh = 0, tt = 0;
q[0] = {sx, sy};
dist[sx][sy] = 0;
while (hh <= tt) {
auto t = q[hh++];
for (int i = 0; i < 8; i++) {
int a = t.x + dx[i], b = t.y + dy[i];
if (a >= 0 && a < n && b >= 0 && b < m && dist[a][b] == -1 && g[a][b] != '*') {
if (g[a][b] == 'H') return dist[t.x][t.y] + 1;
q[++tt] = {a, b};
dist[a][b] = dist[t.x][t.y] + 1;
}
}
}
return -1;
}
int main() {
cin >> m >> n;
for (int i = 0; i < n; i++) cin >> g[i];
cout << bfs() << endl;
return 0;
}
问题描述
分析
N=N+1
才能变为正数,花费的时间变长。N>K
,我们只能用过-1
得到结果,否则我们可以通过三者的组合得到K,并且在过程中数据大小不会超过K+1。+1
或者-1
得到结果。代码
#include
#include
using namespace std;
const int N = 1e5 + 10;
int n, k;
int q[N];
int dist[N];
int bfs() {
memset(dist, -1, sizeof dist);
dist[n] = 0;
q[0] = n;
int hh = 0, tt = 0;
while (hh <= tt) {
int t = q[hh++];
if (t == k) return dist[k];
if (t + 1 < N && dist[t + 1] == -1) {
q[++tt] = t + 1;
dist[t + 1] = dist[t] + 1;
}
if (t - 1 >= 0 && dist[t - 1] == -1) {
q[++tt] = t - 1;
dist[t - 1] = dist[t] + 1;
}
if (t * 2 < N && dist[t * 2] == -1) {
q[++tt] = t * 2;
dist[t * 2] = dist[t] + 1;
}
}
return -1;
}
int main() {
cin >> n >> k;
cout << bfs() << endl;
return 0;
}
问题描述
分析
代码
#include
#include
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010, M = N * N;
int n, m;
char g[N][N];
PII q[M];
int dist[N][N];
void bfs() {
memset(dist, -1, sizeof dist);
// 起点放入队列
int hh = 0, tt = -1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (g[i][j] == '1') {
dist[i][j] = 0;
q[++tt] = {i, j};
}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
while (hh <= tt) {
auto t = q[hh++];
for (int i = 0; i < 4; i++) {
int a = t.x + dx[i], b = t.y + dy[i];
if (a < 1 || a > n || b < 1 || b > m) continue;
if (dist[a][b] != -1) continue;
dist[a][b] = dist[t.x][t.y] + 1;
q[++tt] = {a, b};
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%s", g[i] + 1);
bfs();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) printf("%d ", dist[i][j]);
puts("");
}
return 0;
}
之前的问题都是在一个二维数组中操作,从二维数组中的某个点到达数组中的另外一个点。
下面的两个问题中每个二维数组可以看成一种状态,类似于棋盘,每操作一步转换成一种状态。
问题描述
分析
代码
#include
#include
#include
using namespace std;
int bfs(string start) {
queue<string> q;
unordered_map<string, int> d;
q.push(start);
d[start] = 0;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
string end = "12345678x";
while (q.size()) {
string t = q.front(); q.pop();
if (t == end) return d[t];
int distance = d[t];
int k = t.find('x');
int x = k / 3, y = k % 3; // 一维坐标转换成二维坐标
for (int i = 0; i < 4; i++) {
int a = x + dx[i], b = y + dy[i];
if (a >= 0 && a < 3 && b >= 0 && b < 3) {
swap(t[a * 3 + b], t[k]); // 二维坐标转换成一维坐标
if (!d.count(t)) {
d[t] = distance + 1;
q.push(t);
}
swap(t[a * 3 + b], t[k]);
}
}
}
return -1;
}
int main() {
char s[2];
string start;
for (int i = 0; i < 9; i++) {
cin >> s;
start += *s;
}
cout << bfs(start) << endl;
return 0;
}
问题描述
分析
思路:首先将"12345678"放到队列的队头,然后用宽搜搜索可以到达的状态,直到搜到终点为止。
每一次扩展的时候分别做一下A、B、C三种操作,做完之后得到一个字符串,然后判断这个字符串是否被搜到过,如果没有搜到过的话,把它的距离更新一下,然后存到队列中。
因为要输出方案,我们记录一下每个状态是从哪个状态更新过来的即可。
如何保证存在多个方案时,输出字典序最小的方案呢?我们在扩展的时候按照A、B、C三种方案进行扩展即可。可以使用数学归纳法证明:任意时刻按照A、B、C三种方案进行扩展得到的序列字典序是最小的。
(1)刚开始序列为空,归纳假设显然成立。
(2)假设某一时刻上述归纳假设成立,那么考虑下一时刻:
代码
#include
#include
#include
#include
using namespace std;
char g[2][4]; // 盘面
// pre[s2] = {'A', s1} 表示:s1变到s2是通过A方案实现的
unordered_map<string, pair<char, string>> pre;
unordered_map<string, int> dist;
// 将string转化为盘面
void set(string state) {
for (int i = 0; i < 4; i++) g[0][i] = state[i];
for (int i = 7, j = 0; j < 4; i--, j++) g[1][j] = state[i];
}
// 将盘面转化为string
string get() {
string res;
for (int i = 0; i < 4; i++) res += g[0][i];
for (int i = 3; i >= 0; i--) res += g[1][i];
return res;
}
string move0(string state) {
set(state);
for (int i = 0; i < 4; i++) swap(g[0][i], g[1][i]);
return get();
}
string move1(string state) {
set(state);
int v0 = g[0][3], v1 = g[1][3];
for (int i = 3; i > 0; i--) {
g[0][i] = g[0][i - 1];
g[1][i] = g[1][i - 1];
}
g[0][0] = v0, g[1][0] = v1;
return get();
}
string move2(string state) {
set(state);
int v = g[0][1];
g[0][1] = g[1][1];
g[1][1] = g[1][2];
g[1][2] = g[0][2];
g[0][2] = v;
return get();
}
int bfs(string start, string end) {
if (start == end) return 0;
queue<string> q;
q.push(start);
dist[start] = 0;
while (!q.empty()) {
auto t = q.front(); q.pop();
string m[3];
m[0] = move0(t); // 'A'
m[1] = move1(t); // 'B'
m[2] = move2(t); // 'C'
for (int i = 0; i < 3; i++)
if (!dist.count(m[i])) {
dist[m[i]] = dist[t] + 1;
pre[m[i]] = {i + 'A', t};
q.push(m[i]);
if (m[i] == end) return dist[end];
}
}
return -1;
}
int main() {
int x;
string start, end;
for (int i = 0; i < 8; i++) {
cin >> x;
end += char(x + '0');
}
for (int i = 1; i <= 8; i++) start += char('0' + i);
int step = bfs(start, end);
cout << step << endl;
string res;
while (end != start) {
res += pre[end].first;
end = pre[end].second;
}
reverse(res.begin(), res.end());
if (step > 0) cout << res << endl;
return 0;
}
问题描述
分析
代码
// 非常类似于dijskra
#include
#include
#include
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 550;
int n, m; // 方格的数量为n * m
char g[N][N]; // 存储每个方格内的数据
int dist[N][N]; // 到达每个格点需要的步数
bool st[N][N]; // 判重数组,判断该点是否已求出结果
int bfs() {
memset(dist, 0x3f, sizeof dist);
memset(st, 0, sizeof st);
dist[0][0] = 0;
deque<PII> q;
q.push_back({0, 0});
char cs[] = "\\/\\/"; // 与格点导通的四种情况
int dx[] = {-1, -1, 1, 1}, dy[] = {-1, 1, 1, -1}; // 相邻的四个格点
int ix[] = {-1, -1, 0, 0}, iy[] = {-1, 0, 0, -1}; // 与当前格点相邻的四个方格
while (q.size()) {
PII t = q.front(); q.pop_front();
if (st[t.x][t.y]) continue; // 当前点已经求出最短距离
st[t.x][t.y] = true;
for (int i = 0; i < 4; i++) {
int a = t.x + dx[i], b = t.y + dy[i];
if (a < 0 || a > n || b < 0 || b > m) continue;
int ca = t.x + ix[i], cb = t.y + iy[i]; // 考察与当前格点相邻的第i个方格
int w = (g[ca][cb] != cs[i]); // 不能导通,需要旋转一次
int d = dist[t.x][t.y] + w;
// 因为某个格点可能入队多次,需要更新dist[a][b],更新后的数据需要入队
if (d < dist[a][b]) {
dist[a][b] = d;
if (w) q.push_back({a, b});
else q.push_front({a, b});
}
}
}
return dist[n][m];
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) scanf("%s", g[i]);
if ((n + m) & 1) puts("NO SOLUTION");
else printf("%d\n", bfs());
}
return 0;
}
问题描述
分析
上图来自:题解
代码
#include
#include
#include
using namespace std;
const int N = 6;
int n; // 规则的数目
string a[N], b[N]; // 变换规则,从a变为b
// q: 需要扩展的队列; da: 扩展的新状态距离存储的位置
// db: 检查新状态另一侧是否已经扩展到了
int extend(queue<string> &q, unordered_map<string, int> &da,
unordered_map<string, int> &db, string a[], string b[]) {
for (int k = 0, sk = q.size(); k < sk; k++) {
string t = q.front();
q.pop();
// 针对每一个字符串进行扩展
for (int i = 0; i < t.size(); i++)
for (int j = 0; j < n; j++)
if (t.substr(i, a[j].size()) == a[j]) {
string state = t.substr(0, i) + b[j] + t.substr(i + a[j].size());
if (da.count(state)) continue;
if (db.count(state)) return da[t] + 1 + db[state];
da[state] = da[t] + 1;
q.push(state);
}
}
return 11;
}
int bfs(string A, string B) {
queue<string> qa, qb; // qa从起点开始扩展,qb从终点开始扩展
unordered_map<string, int> da, db; // 记录扩展步数,同时还有判重的作用
qa.push(A), da[A] = 0;
qb.push(B), db[B] = 0;
while (qa.size() && qb.size()) {
int t;
if (qa.size() <= qb.size()) t = extend(qa, da, db, a, b);
else t = extend(qb, db, da, b, a);
// 如果t>10,存在两种情况: (1) 还没扩展完毕; (2) 实际步数确实大于10
if (t <= 10) return t;
}
return 11;
}
int main() {
string A, B;
cin >> A >> B;
while (cin >> a[n] >> b[n]) n++;
int step = bfs(A, B);
if (step > 10) puts("NO ANSWER!");
else cout << step << endl;
return 0;
}
A*算法原理
问题描述
分析
这一题的估价函数:每个点到终点的最短距离。这个可以反向求解,只需要在反向图上跑一边dijkstra
算法即可。
那么如何求解第K短路呢?
当终点第一次从队列中弹出来的时候,一定是最短路径(上面A*算法中已经证明了)。因此可以猜想,当终点从队列中第K次弹出来的时候,就是第K短路径,这个猜想的证明思路是类似的,只需要证明第二次弹出的是第二小的即可,也可以使用反证法,将上面A*算法证明中的最小值换成第二小值即可。
代码
#include
#include
#include
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
typedef pair<int, PII> PIII;
const int N = 1010, M = 200010;
int n, m, S, T, K;
// 正向邻接表表头,反向邻接表表头,边,边权,next指针
int h[N], rh[N], e[M], w[M], ne[M], idx;
int dist[N]; // 记录反向表中终点到其他各个点的距离,估价函数的作用
int cnt[N]; // 记录每个点在队列中出现的次数
bool st[N]; // dijkstra过程中的判重数组
void add(int h[], int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
// 在反向图中运行dijkstra,求出终点到其余各点的最短路径
void dijkstra() {
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, T}); // (距离T的距离,当前点)
memset(dist, 0x3f, sizeof dist);
dist[T] = 0;
while (heap.size()) {
auto t = heap.top(); heap.pop();
int ver = t.y;
// BFS入队的时候就可以确定该点的最短路径
// dijkstra在出队的时候可以确定该点的最短路径
// astar只有终点在出队的时候可以确定该点最短路径
if (st[ver]) continue;
st[ver] = true;
for (int i = rh[ver]; ~i; i = ne[i]) {
int j = e[i]; // ver->j, 权重为w[i]
if (dist[j] > dist[ver] + w[i]) {
dist[j] = dist[ver] + w[i];
heap.push({dist[j], j});
}
}
}
}
int astar() {
priority_queue<PIII, vector<PIII>, greater<PIII>> heap;
// (当前点v到起点的真实值+v到终点的估计值, (v到起点的真实值, v))
heap.push({dist[S], {0, S}});
while (heap.size()) {
auto t = heap.top(); heap.pop();
int ver = t.y.y, distance = t.y.x;
cnt[ver]++;
if (cnt[T] == K) return distance;
// 扩展ver的相邻顶点,只要点在队列中的次数小于K,就要加入队列
for (int i = h[ver]; ~i; i = ne[i]) {
int j = e[i];
if (cnt[j] < K) heap.push({distance + w[i] + dist[j], {distance + w[i], j}});
}
}
return -1;
}
int main() {
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
memset(rh, -1, sizeof rh);
for (int i = 0; i < m; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(h, a, b, c);
add(rh, b, a, c);
}
scanf("%d%d%d", &S, &T, &K);
if (S == T) K++; // 每条最短路中至少要包含一条边
dijkstra();
printf("%d\n", astar());
return 0;
}
问题描述
分析
代码
#include
#include
#include
#include
#define x first
#define y second
using namespace std;
typedef pair<int, string> PIS; // (到终点的估计值, 当前状态)
// 估计函数
int f(string state) {
int res = 0;
for (int i = 0; i <= 8; i++)
if (state[i] != 'x') {
// 左上角的点对应应该防止1,这里方便计算从0开始计算
// 放置在位置i上的数字目前为v
int v = state[i] - '1';
res += abs(i / 3 - v / 3) + abs(i % 3 - v % 3);
}
return res;
}
string bfs(string start) {
string end = "12345678x";
unordered_map<string, int> dist;
// pre[s2] = {'u', s1}表示: s1通过操作u得到s2
unordered_map<string, pair<char, string>> pre;
priority_queue<PIS, vector<PIS>, greater<PIS>> heap;
heap.push({f(start), start});
dist[start] = 0;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
char op[5] = "urdl"; // 上右下左
while (heap.size()) {
auto t = heap.top(); heap.pop();
string state = t.y;
if (state == end) break;
// 找到'x'所在位置
int x, y;
for (int i = 0; i < 9; i++)
if (state[i] == 'x') {
x = i / 3, y = i % 3;
break;
}
string source = state;
for (int i = 0; i < 4; i++) {
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= 3 || b < 0 || b >= 3) continue;
state = source;
swap(state[x * 3 + y], state[a * 3 + b]);
if (dist.count(state) == 0 || dist[state] > dist[source] + 1) {
dist[state] = dist[source] + 1;
pre[state] = {op[i], source};
heap.push({dist[state] + f(state), state});
}
}
}
string res;
while (end != start) {
res += pre[end].x;
end = pre[end].y;
}
reverse(res.begin(), res.end());
return res;
}
int main() {
string start, seq;
char c;
while (cin >> c) {
start += c;
if (c != 'x') seq += c;
}
int cnt = 0;
for (int i = 0; i < 8; i++)
for (int j = i + 1; j < 8; j++)
if (seq[i] > seq[j])
cnt++;
if (cnt % 2) puts("unsolvable");
else cout << bfs(start) << endl;
return 0;
}
问题描述
分析
代码
/**
* 执行用时:160 ms, 在所有 C++ 提交中击败了59.48%的用户
* 内存消耗:15.7 MB, 在所有 C++ 提交中击败了34.35%的用户
*/
class Solution {
public:
unordered_set<string> S;
unordered_map<string, int> dist; // 距离数组,同时具有判重的作用
queue<string> q;
int ladderLength(string beginWord, string endWord, vector<string> &wordList) {
for (auto word : wordList) S.insert(word);
dist[beginWord] = 0;
q.push(beginWord);
while (q.size()) {
auto t = q.front();
q.pop();
string r = t;
for (int i = 0; i < t.size(); i++) {
t = r;
for (char j = 'a'; j <= 'z'; j++) {
t[i] = j;
if (S.count(t) && dist.count(t) == 0) {
dist[t] = dist[r] + 1;
if (t == endWord) break;
q.push(t);
}
}
}
}
return dist[endWord] != 0 ? dist[endWord] + 1 : 0;
}
};
/**
* Date: 2021/3/18 10:01
* 执行用时:283 ms, 在所有 Java 提交中击败了35.26%的用户
* 内存消耗:42.1 MB, 在所有 Java 提交中击败了18.79%的用户
*/
class Solution {
HashSet<String> S = new HashSet<>();
HashMap<String, Integer> dist = new HashMap<>();
Queue<String> q = new LinkedList<>();
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
S.addAll(wordList);
q.add(beginWord);
dist.put(beginWord, 0);
while (!q.isEmpty()) {
String t = q.remove();
for (int i = 0; i < t.length(); i++)
for (char j = 'a'; j <= 'z'; j++) {
String r = t.substring(0, i) + j + t.substring(i + 1);
if (S.contains(r) && !dist.containsKey(r)) {
dist.put(r, dist.get(t) + 1);
if (endWord.equals(r)) break;
q.add(r);
}
}
}
return dist.getOrDefault(endWord, -1) + 1;
}
}
问题描述
分析
代码
/**
* 执行用时:144 ms, 在所有 C++ 提交中击败了76.80%的用户
* 内存消耗:15.5 MB, 在所有 C++ 提交中击败了75.43%的用户
*/
class Solution {
public:
unordered_set<string> S;
unordered_map<string, int> dist; // 距离数组,同时具有判重的作用
queue<string> q;
vector<vector<string>> ans;
vector<string> path;
string beginWord;
vector<vector<string>> findLadders(string _beginWord, string endWord, vector<string> &wordList) {
for (auto word : wordList) S.insert(word);
beginWord = _beginWord;
dist[beginWord] = 0;
q.push(beginWord);
while (q.size()) {
auto t = q.front();
q.pop();
string r = t;
for (int i = 0; i < t.size(); i++) {
t = r;
for (char j = 'a'; j <= 'z'; j++) {
t[i] = j;
if (S.count(t) && dist.count(t) == 0) {
dist[t] = dist[r] + 1;
if (t == endWord) break;
q.push(t);
}
}
}
}
if (!dist.count(endWord)) return ans;
path.push_back(endWord);
dfs(endWord);
return ans;
}
void dfs(string t) {
if (t == beginWord) {
reverse(path.begin(), path.end());
ans.push_back(path);
reverse(path.begin(), path.end());
} else {
string r = t;
for (int i = 0; i < t.size(); i++) {
t = r;
for (char j = 'a'; j <= 'z'; j++) {
t[i] = j;
// 不能使用S.count(t), 因为起点不一定在S中
// dist[t] 表示t到起点的距离, 这里进行了剪枝
if (dist.count(t) && dist[t] + 1 == dist[r]) {
path.push_back(t);
dfs(t);
path.pop_back();
}
}
}
}
}
};
/**
* Created by WXX on 2021/3/18 9:29
* 执行用时:254 ms, 在所有 Java 提交中击败了58.92%的用户
* 内存消耗:42.3 MB, 在所有 Java 提交中击败了78.18%的用户
*/
public class Solution {
HashSet<String> S = new HashSet<>();
HashMap<String, Integer> dist = new HashMap<>();
Queue<String> q = new LinkedList<>();
List<List<String>> ans = new ArrayList<>();
ArrayList<String> path = new ArrayList<>();
String beginWord;
public List<List<String>> findLadders(String _beginWord, String endWord, List<String> wordList) {
S.addAll(wordList);
beginWord = _beginWord;
q.add(beginWord);
dist.put(beginWord, 0);
while (!q.isEmpty()) {
String t = q.remove();
for (int i = 0; i < t.length(); i++)
for (char j = 'a'; j <= 'z'; j++) {
String r = t.substring(0, i) + j + t.substring(i + 1);
if (S.contains(r) && !dist.containsKey(r)) {
dist.put(r, dist.get(t) + 1);
if (endWord.equals(r)) break;
q.add(r);
}
}
}
if (!dist.containsKey(endWord)) return ans;
path.add(endWord);
dfs(endWord);
return ans;
}
private void dfs(String t) {
if (beginWord.equals(t)) {
Collections.reverse(path);
ans.add((List<String>) path.clone());
Collections.reverse(path);
} else {
for (int i = 0; i < t.length(); i++)
for (char j = 'a'; j <= 'z'; j++) {
String r = t.substring(0, i) + j + t.substring(i + 1);
if (dist.containsKey(r) && dist.get(r) + 1 == dist.get(t)) {
path.add(r);
dfs(r);
path.remove(path.size() - 1);
}
}
}
}
}
问题描述
分析
代码
/**
* 执行用时:892 ms, 在所有 C++ 提交中击败了33.96%的用户
* 内存消耗:174.7 MB, 在所有 C++ 提交中击败了10.38%的用户
*/
class Solution {
public:
struct Tree {
int x, y, h; // 位于(x, y)的树的高度为h
bool operator<(const Tree &t) const {
return h < t.h;
}
};
int n, m;
vector<vector<int>> g;
int bfs(Tree st, Tree ed) {
if (st.x == ed.x && st.y == ed.y) return 0; // 起点终点都是出发点
queue<Tree> q;
vector<vector<int>> dist(n, vector<int>(m, -1));
dist[st.x][st.y] = 0;
q.push(st);
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
while (q.size()) {
auto t = q.front();
q.pop();
for (int i = 0; i < 4; i++) {
int x = t.x + dx[i], y = t.y + dy[i];
if (x >= 0 && x < n && y >= 0 && y < m && g[x][y]) {
if (dist[x][y] == -1) { // 没有判重数组,必须有这句话
dist[x][y] = dist[t.x][t.y] + 1;
if (x == ed.x && y == ed.y) return dist[x][y];
q.push({x, y});
}
}
}
}
return -1;
}
int cutOffTree(vector<vector<int>> &forest) {
g = forest;
n = g.size(), m = g[0].size();
vector<Tree> trs;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (g[i][j] > 1)
trs.push_back({i, j, g[i][j]});
sort(trs.begin(), trs.end());
Tree last = {0, 0};
int res = 0;
for (auto &tr : trs) {
int t = bfs(last, tr); // last -> tr
if (t == -1) return -1;
res += t;
last = tr; // 更新last
}
return res;
}
};
/**
* Created by WXX on 2021/3/7 18:51
* 执行用时:242 ms, 在所有 Java 提交中击败了96.69%的用户
* 内存消耗:38.7 MB, 在所有 Java 提交中击败了87.60%的用户
*/
public class Solution {
static class Tree implements Comparable<Tree> {
int x, y, h;
public Tree(int x, int y, int h) {
this.x = x;
this.y = y;
this.h = h;
}
@Override
public int compareTo(Tree o) {
return this.h - o.h;
}
}
int n, m;
int[][] g;
private int bfs(Tree st, Tree ed) {
if (st.x == ed.x && st.y == ed.y) return 0;
Queue<Tree> q = new LinkedList<>();
int[][] dist = new int[n][m];
for (int i = 0; i < n; i++) Arrays.fill(dist[i], 0x3f3f3f3f);
dist[st.x][st.y] = 0;
q.add(st);
int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
while (!q.isEmpty()) {
Tree t = q.remove();
for (int i = 0; i < 4; i++) {
int x = t.x + dx[i], y = t.y + dy[i];
if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] != 0) {
if (dist[x][y] > dist[t.x][t.y] + 1) {
dist[x][y] = dist[t.x][t.y] + 1;
if (x == ed.x && y == ed.y) return dist[x][y];
q.add(new Tree(x, y, -1));
}
}
}
}
return -1;
}
public int cutOffTree(List<List<Integer>> forest) {
n = forest.size();
m = forest.get(0).size();
g = new int[n][m];
List<Tree> trs = new ArrayList<>();
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++) {
g[i][j] = forest.get(i).get(j);
if (g[i][j] > 1) trs.add(new Tree(i, j, g[i][j]));
}
Collections.sort(trs);
Tree last = new Tree(0, 0, -1);
int res = 0;
for (Tree tr : trs) {
int t = bfs(last, tr);
if (t == -1) return -1;
res += t;
last = tr;
}
return res;
}
}