扩点最短路
扩点最短路
不把实际位置看作图上的点,而是把实际位置和该位置的所有状态的组合看作是图上的点,BFS 或者 Dijkstra 的过程不变,只是增加了一些点。
864. 获取所有钥匙的最短路径
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
class Solution {
public:
int rows, columns;
// 最多 6 把钥匙
int max_k = 6;
int key = 0;
vector<int> move{-1, 0, 1, 0, -1};
bool isCoordinateLegal(int row, int column) {
return row >= 0 && row < rows && column >= 0 && column < columns;
}
// BFS
int shortestPathAllKeys(vector<string> &grid) {
rows = grid.size();
columns = grid[0].size();
// (x, y, 持有钥匙的状态)
queue<vector<int>> q;
// 找起点
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < columns; ++j) {
if (grid[i][j] == '@') {
q.emplace(vector<int>{i, j, 0});
} else if (grid[i][j] >= 'a' && grid[i][j] <= 'f') {
// 计算获得所有钥匙的最终状态
key |= (1 << (grid[i][j] - 'a'));
}
}
}
// 表示当前位置的某个状态是否从队列中弹出过,状态是指持有钥匙的情况
vector<vector<vector<bool>>> visited(rows, vector<vector<bool>>(columns));
for (int i = 0; i < rows; ++i)
for (int j = 0; j < columns; ++j)
visited[i][j].resize(key, false);
int step = 1;
while (!q.empty()) {
int size = q.size();
// 逐层弹出
for (int i = 0; i < size; ++i) {
auto f = q.front();
q.pop();
int x = f[0];
int y = f[1];
// 经过(x, y)后,到达下个点前持有钥匙的状态
int s = f[2];
// 四周
for (int j = 0; j < 4; ++j) {
int nx = x + move[j];
int ny = y + move[j + 1];
// 越界
if (!isCoordinateLegal(nx, ny)) continue;
char ch = grid[nx][ny];
// 墙
if (ch == '#') continue;
// 锁,且没对应钥匙
if (ch >= 'A' && ch <= 'F' && (s & (1 << (ch - 'A'))) == 0) continue;
// 钥匙,更新持有的钥匙状态
int ns = s;
if (ch >= 'a' && ch <= 'f')
ns |= (1 << (ch - 'a'));
// 获得所有钥匙了
if (ns == key) return step;
if (visited[nx][ny][ns]) continue;
visited[nx][ny][ns] = true;
q.emplace(vector<int>{nx, ny, ns});
}
}
step++;
}
return -1;
}
};
LCP 35. 电动车游城市
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
class Solution {
public:
struct cmp {
bool operator()(vector<int> &v1, vector<int> &v2) {
return v1[2] > v2[2];
}
};
int electricCarPlan(vector<vector<int>> &paths, int cnt, int start, int end, vector<int> &charge) {
int n = charge.size();
vector<vector<pair<int, int>>> graph(n);
// 无向图
for (const auto &item: paths) {
graph[item[0]].emplace_back(make_pair(item[1], item[2]));
graph[item[1]].emplace_back(make_pair(item[0], item[2]));
}
// (点, 到这个点的剩余电量, 代价)
priority_queue<vector<int>, vector<vector<int>>, cmp> heap;
// vector[i][j] 为到达 i 点时剩余 j 电量的最小代价
vector<vector<int>> distance(n, vector<int>(cnt + 1, INT_MAX));
// 访问标记
vector<vector<bool>> visited(n, vector<bool>(cnt + 1, false));
heap.emplace(vector<int>{start, 0, 0});
distance[start][0] = 0;
while (!heap.empty()) {
auto top = heap.top();
heap.pop();
int position = top[0];
int power = top[1];
int cost = top[2];
// 结束
if (position == end) return cost;
if (visited[position][power]) continue;
visited[position][power] = true;
// 充一格电,到图中扩出来的点,也就是这个点的其他状态
if (power < cnt
&& !visited[position][power + 1]
&& (cost + charge[position] < distance[position][power + 1])) {
distance[position][power + 1] = cost + charge[position];
heap.emplace(vector<int>{position, power + 1, distance[position][power + 1]});
}
// 不充电,直接去别的点
for (const auto &item: graph[position]) {
int nextPosition = item.first;
int restPower = power - item.second;
int nextCost = cost + item.second;
// 到不了下个点,或者下个状态已经弹出过,就跳过
if (restPower < 0 || visited[nextPosition][restPower]) continue;
if (nextCost < distance[nextPosition][restPower]) {
distance[nextPosition][restPower] = nextCost;
heap.emplace(vector<int>{nextPosition, restPower, nextCost});
}
}
}
return -1;
}
};
P4568 [JLOI2011] 飞行路线
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
int n, m, k, s, t;
vector<int> head;
vector<int> nxt;
vector<int> to;
vector<int> weight;
int cnt;
void build() {
head.resize(n, 0);
fill(head.begin(), head.end(), 0);
nxt.resize((m << 1) + 1);
to.resize((m << 1) + 1);
weight.resize((m << 1) + 1);
cnt = 1;
}
void addEdge(int u, int v, int w) {
nxt[cnt] = head[u];
to[cnt] = v;
weight[cnt] = w;
head[u] = cnt;
cnt++;
}
struct cmp {
bool operator()(vector<int> &v1, vector<int> &v2) {
return v1[2] > v2[2];
}
};
int main() {
cin >> n >> m >> k >> s >> t;
build();
for (int i = 0, u, v, w; i < m; ++i) {
cin >> u >> v >> w;
addEdge(u, v, w);
addEdge(v, u, w);
}
// distance[i][j] 为到达 i 点,剩余免费乘坐次数 j 次的最少代价
vector<vector<int>> distance(n, vector<int>(k + 1, 0x7fffffff));
// 访问标记
vector<vector<bool>> visited(n, vector<bool>(k + 1, false));
// (点,到点后剩余的免费次数,最少代价)
priority_queue<vector<int>, vector<vector<int>>, cmp> heap;
distance[s][k] = 0;
heap.emplace(vector<int>{s, k, 0});
while (!heap.empty()) {
auto top = heap.top();
heap.pop();
int u = top[0];
int free = top[1];
int cost = top[2];
// 结束
if (u == t) {
cout << cost;
return 0;
}
if (visited[u][free]) continue;
visited[u][free] = true;
for (int ei = head[u]; ei > 0; ei = nxt[ei]) {
int v = to[ei];
int w = weight[ei];
// 使用一张票
if (free > 0
&& !visited[v][free - 1]
&& cost < distance[v][free - 1]) {
distance[v][free - 1] = cost;
heap.emplace(vector<int>{v, free - 1, cost});
}
// 不使用
if (!visited[v][free]
&& cost + w < distance[v][free]) {
distance[v][free] = cost + w;
heap.emplace(vector<int>{v, free, cost + w});
}
}
}
}