Dijkstra算法:给定一个源点,求解从源点到每个点的最短的路径长度。
适用范围:有向图,边的权值没有负数
普通堆实现dijkstra:最普遍,最常用;时间复杂度O(m*logm),m为边数
1.distanc[i]表示从源点到i点的最短距离,visit[i]表示节点i是否从小根堆里弹出过
2.准备好小根堆,小根堆存放记录:(x,x到源点的距离),小根堆根据距离组织
3.令distancs[源点]=0,(源点,0)进入堆
4.从小根堆里弹出:(u,w)
如果visit[u]=true,不做处理,重复步骤4;
如果visit[u]=false,令visit[u]=true,考察u的每一条边(v,m)
如果visit[v]=false并且distance[u]+m<distance[v],令distance[v]=distance[u]+m,加入小根堆;重复步骤4
5.直至小根堆为空,distance记录了源点到各点的最短距离
class Solution {
public:
static const int maxnum = 100;
int distance[maxnum];
bool visit[maxnum];
vector<vector<vector<int>>> pragh;//动态建图
void build(int n) {
for (int i = 0; i <= n; i++) {
pragh.push_back(vector<vector<int>>());
distance[i] = INT_MAX;
visit[i] = false;
}
}
int networkDelayTime(vector<vector<int>>& times, int n, int k) {
build(n);
for (auto edge : times) {
pragh[edge[0]].push_back({edge[1], edge[2]});
}
distance[k] = 0;
auto cmp = [](vector<int> a, vector<int> b) { return a[1] > b[1]; };
priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)> heap(
cmp);
heap.push({k, 0});
while (!heap.empty()) {
vector<int> re = heap.top();
heap.pop();
int u = re[0];
int w = re[1];
if (visit[u])//弹出过就跳过
continue;
visit[u] = true;
for (auto edge : pragh[u]) {
int v = edge[0];
int m = edge[1];
if (!visit[v] && distance[u] + m < distance[v]) {//
distance[v] = distance[u] + m;
heap.push({v, distance[v]});//加入更小修改之后的边
}
}
}
int ans = INT_MIN;
for (int i = 1; i <= n; i++) {
if (distance[i] == INT_MAX)
return -1;
ans = max(ans, distance[i]);
}
return ans;
}
};
反向索引堆实现Dijkstra算法:时间复杂度为(m*logn),n为节点数,m为边数
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
const int maxn = 100001;
const int maxm = 200001;
int head[maxn];
int nex[maxm];
int to[maxm];
int value[maxm];
int cnt;
int heap[maxn];
int where[maxn];
int heapsize;
int distanc[maxn];
int n, m, s;
void build() {//建图
for (int i = 1; i <= n; i++) {
head[i] = 0;
distanc[i] = INT_MAX;
where[i] = -1;
}
cnt = 1;
}
void insert(int u, int v, int w) {//插入图
nex[cnt] = head[u];
to[cnt] = v;
value[cnt] = w;
head[u] = cnt++;
}
void swap(int i, int j) {//交换堆
int rem = heap[i];
heap[i] = heap[j];
heap[j] = rem;
where[heap[i]] = i;
where[heap[j]] = j;
}
void heapify(int i) {//向下调整堆
int l = 2 * i + 1;
while (l < heapsize) {
int best = l + 1 < heapsize && distanc[heap[l + 1]] < distanc[heap[l]] ? l + 1 : l;
best = distanc[heap[best]] < distanc[heap[i]] ? best : i;
if (best == i) break;
swap(i, best);
i = best;
l = 2 * i + 1;
}
}
int pop() {//弹出节点
int ans = heap[0];
swap(0, --heapsize);
heapify(0);
where[ans] = -2;
return ans;
}
void heapinsert(int i) {//堆插入
while (distanc[heap[i]] < distanc[heap[(i-1)/2]]) {
swap(i, (i - 1) / 2);
i = (i - 1) / 2;
}
}
void addoringorne(int v, int w) {
if (where[v] == -1) {
heap[heapsize] = v;
where[v] = heapsize++;
distanc[v] = w;
heapinsert(where[v]);
}
else if (where[v] >= 0) {
distanc[v] = min(distanc[v], w);
heapinsert(where[v]);
}
}
int main() {
cin >> n >> m >> s;
build();
while (m--) {
int a, b, c;
cin >> a >> b >> c;
insert(a, b, c);
}
distanc[s] = 0;
addoringorne(s, 0);
while (heapsize > 0) {
int v = pop();
for (int i = head[v]; i != 0; i = nex[i]) {
addoringorne(to[i], distanc[v] + value[i]);
}
}
for (int i = 1; i <= n; i++)
cout << distanc[i] << " ";
return 0;
}
class Solution {
public:
int move[5] = {-1, 0, 1, 0, -1};
bool visited[101][101] = {0};
int minimumEffortPath(vector<vector<int>>& heights) {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n = heights.size();
int m = heights[0].size();
auto cmp = [](vector<int> a, vector<int> b) { return a[2] > b[2]; };
priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)> heap(
cmp);
heap.push({0, 0, 0});
while (!heap.empty()) {
vector<int> rem = heap.top();
heap.pop();
int x = rem[0];
int y = rem[1];
int w = rem[2];
if (x == n - 1 && y == m - 1)
return w;
visited[x][y] = true;
for (int i = 0; i < 4; i++) {
int nx = x + move[i];
int ny = y + move[i + 1];
if (nx >= 0 && nx < n && ny >= 0 && ny < m &&
visited[nx][ny] == false) {
int r = abs(heights[x][y] - heights[nx][ny]);
int nw = max(w, r);
heap.push({nx, ny, nw});
}
}
}
return -1;
}
};
class Solution {
public:
static const int maxn = 31;
static const int maxm = 31;
static const int maxk = 6;
int move[5] = {-1, 0, 1, 0, -1};
bool visited[maxn][maxm][1 << maxk];
int queue[maxm * maxn * (1 << maxk)][3];
int l, r, n, m, key;
void build(vector<string>& grid) {
n = grid.size();
m = grid[0].size();
key = 0;
l = 0, r = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '@') {
queue[r][0] = i;
queue[r][1] = j;
queue[r++][2] = 0;
}
if (grid[i][j] >= 'a' && grid[i][j] <= 'f') {
key |= (1 << (grid[i][j] - 'a'));
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
for (int k = 0; k <= key; k++)
visited[i][j][k] = false;
}
}
}
int shortestPathAllKeys(vector<string>& grid) {
build(grid);
int level = 1;
while (l < r) {
int size = r - l;
while (size--) {
int x = queue[l][0];
int y = queue[l][1];
int ke = queue[l++][2];
for (int i = 0; i < 4; i++) {
int nx = x + move[i];
int ny = y + move[i + 1];
int nke = ke;
if (nx >= 0 && ny >= 0 && nx < n && ny < m &&
grid[nx][ny] != '#') {
if (grid[nx][ny] >= 'A' && grid[nx][ny] <= 'F' &&(((nke>>(grid[nx][ny]-'A')&1)==0)))
continue;
if (grid[nx][ny] >= 'a' && grid[nx][ny] <= 'f')
nke |= (1 << (grid[nx][ny] - 'a'));
if (nke == key)
return level;
if (!visited[nx][ny][nke]) {
visited[nx][ny][nke] = true;
queue[r][0] = nx;
queue[r][1] = ny;
queue[r++][2] = nke;
}
}
}
}
level++;
}
return -1;
}
};
到达每一个点时,在同一个点上,状态也不同,即持有的钥匙不同,所以把在位置上的点和状态结合作为一种新的点进行扩展。求收集满这个点的最短距离。每条边的权值都是1,所以用bfs找最短距离
class Solution {
public:
static const int maxn=101;
int distance[maxn][maxn];
bool visited[maxn][maxn];
vector<vector<vector<int>>>pragh;
void build(int n,int cnt){
for(int i=0;i<n;i++){
pragh.push_back(vector<vector<int>>());
for(int j=0;j<=cnt;j++){
distance[i][j]=INT_MAX;
visited[i][j]=false;
}
}
}
void insert(int u,int v,int w){
pragh[u].push_back({v,w});
pragh[v].push_back({u,w});
}
int electricCarPlan(vector<vector<int>>& paths, int cnt, int start, int end, vector<int>& charge) {
int n=charge.size();
build(n,cnt);
for(auto edge:paths)
insert(edge[0],edge[1],edge[2]);
auto cmp=[](vector<int>a,vector<int>b){return a[2]>b[2];};
priority_queue<vector<int>,vector<vector<int>>,decltype(cmp)>heap(cmp);
distance[start][0]=0;
heap.push({start,0,0});
while(!heap.empty()){
vector<int>rem=heap.top();
heap.pop();
int cur=rem[0];
int power=rem[1];
int cost=rem[2];
if(visited[cur][power])
continue;
if(cur==end) return cost;
visited[cur][power]=true;
if(power<cnt){//充电,只充一个电,如果还要充继续迭代
if(!visited[cur][power+1]&&cost+charge[cur]<distance[cur][power+1]){
distance[cur][power+1]=cost+charge[cur];
heap.push({cur,power+1,distance[cur][power+1]});
}
}
for(auto edge:pragh[cur]){//不充电
int next=edge[0];
int restpower=power-edge[1];
int nextcost=cost+edge[1];
if(restpower>=0&&!visited[next][restpower]&&nextcost<distance[next][restpower]){
distance[next][restpower]=nextcost;
heap.push({next,restpower,nextcost});
}
}
}
return -1;
}
};
每个节点的状态除了点不同,还有剩余的电量(花费的时间是由剩余的电量和不同的边决定的,不属于决定性变量)。由于边权值的不同,用dijkstra求最小花费即可
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;
int n, m, k, s, t;
const int maxn = 10001;
const int maxk = 11;
int distanc[maxn][maxk];
bool visited[maxn][maxk];
vector<vector<vector<int>>>pragh;
void build(int n,int k) {
for (int i = 0; i < n; i++) {
pragh.push_back(vector<vector<int>>());
for (int j = 0; j <= k; j++) {
distanc[i][j] = INT_MAX;
visited[i][j] = false;
}
}
}
void insert(int u, int v, int w) {
pragh[u].push_back({ v,w });
pragh[v].push_back({ u,w });
}
int main() {
cin >> n >> m >> k >> s >> t;
build(n, k);
while (m--) {
int a, b, c;
cin >> a >> b >> c;
insert(a, b, c);
}
auto cmp = [](vector<int>a, vector<int>b) {return a[2] > b[2]; };
priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)>heap(cmp);
distanc[s][0] = 0;
heap.push({ s,0,0 });
while (!heap.empty()) {
vector<int>rem = heap.top();
heap.pop();
int cur = rem[0];
int num = rem[1];
int cost = rem[2];
if (cur == t) {
cout << cost;
return 0;
}
if (visited[cur][num]) continue;
visited[cur][num] = true;
for (auto edge : pragh[cur]) {
int nextnode = edge[0];
int nums1 = num + 1;
int nums2 = num;
int nextcost1 = cost;
int nextcost2 = cost + edge[1];
if (!visited[nextnode][nums1] && nextcost1 < distanc[nextnode][nums1]) {
distanc[nextnode][nums1] = nextcost1;
heap.push({ nextnode,nums1,nextcost1 });
}
if (!visited[nextnode][nums2] && nextcost1 < distanc[nextnode][nums2]) {
distanc[nextnode][nums2] = nextcost2;
heap.push({ nextnode,nums2,nextcost2 });
}
}
}
return 0;
}
和上题的思路基本一致,用dijkstra算法分层拓展求解即可