# Chữa bài dijkstra 08/08/2023 > Lưu ý: > - Dùng `priority_queue` sẽ nhanh hơn `set`. > - Những bài toán cần trạng thái dijkstra nhiều chiều, có thể dùng struct như sau: > ```c++ struct state{ int w, a, b, c, d, x, y, z.... bool operator < (const state &a) const{ return w > a.w; } }; priority_queue<state> q; ``` > hoặc dùng `tuple`. ## Bài 1: [Xóa cạnh](https://marisaoj.com/problem/176) - Trạng thái $f(i,j)$ là đường đi ngắn nhất tới $i$ và đã xóa $j$ cạnh. Code: :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; #define pi pair<int, int> #define inf32 0x3f3f3f3f #define inf64 0x3f3f3f3f3f3f3f3f #define all(x) x.begin(), x.end() #define Unique(v) v.erase(unique(all(v)), v.end()) #define int long long #define setinf(d) memset(d, inf32, sizeof d) #define setneg(d) memset(d, -1, sizeof d) #define set0(d) memset(d, 0, sizeof d) #define Log2(x) 63 - __builtin_clzll(x) #define oo 2e18 #define mod 1000000007 #define FILENAME "f" const int maxn = 2e5 + 5; int n, m, k; int d[maxn][6]; vector<pi> edge[maxn]; struct state{ int w, u, k; bool operator < (const state &a) const{ return w > a.w; } }; void dij(){ setinf(d); priority_queue<state> q; d[1][0] = 0; q.push({0, 1, 0}); while(q.size()){ int w = q.top().w, u = q.top().u, K = q.top().k; q.pop(); if(d[u][K] != w) continue; for(pi &v : edge[u]){ if(d[v.first][K] > w + v.second){ q.push({d[v.first][K] = w + v.second, v.first, K}); } if(K < k && d[v.first][K + 1] > w){ q.push({d[v.first][K + 1] = w, v.first, K + 1}); } } } int mn = inf64; for(int i = 0; i <= k; i++) mn = min(d[n][i], mn); if(mn == inf64) cout << -1; else cout << mn; } signed main(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> m >> k; for(int i = 1; i <= m; i++){ int u,v , w; cin >> u >> v >> w; edge[u].push_back({v, w}); edge[v].push_back({u, w}); } dij(); } ``` ::: ## Bài 2: [Đường đi ba chiều](https://marisaoj.com/problem/178) - Trạng thái $f(x,y,z)$ là đường đi ngắn nhất tới tọa độ $(x,y,z)$. Code :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; long long n, a[107][107][107], dist[107][107][107]; bool visited[107][107][107]; struct NODE{ long long w, x, y, z; }; struct cmp{ bool operator() (NODE a, NODE b){ return a.w > b.w; } }; struct VERTEX{ int x, y, z; }; vector<VERTEX> adj[107][107][107]; void dijkstra(int x, int y, int z){ memset(dist, 0x3f, sizeof dist); memset(visited, false, sizeof visited); dist[x][y][z] = 0; priority_queue<NODE, vector<NODE>, cmp> pq; pq.push({dist[x][y][z], x, y, z}); while(!pq.empty()){ NODE u = pq.top(); pq.pop(); if(visited[u.x][u.y][u.z]) continue; visited[u.x][u.y][u.z] = true; for(int i = 0; i < (int) adj[u.x][u.y][u.z].size(); i++){ VERTEX v = adj[u.x][u.y][u.z][i]; if(dist[v.x][v.y][v.z] > dist[u.x][u.y][u.z] + a[v.x][v.y][v.z]){ dist[v.x][v.y][v.z] = dist[u.x][u.y][u.z] + a[v.x][v.y][v.z]; pq.push({dist[v.x][v.y][v.z], v.x, v.y, v.z}); } } } } int main(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n; for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ for(int k = 1; k <= n; k++){ cin >> a[i][j][k]; if(i > 1) adj[i][j][k].push_back({i - 1, j, k}); if(j > 1) adj[i][j][k].push_back({i, j - 1, k}); if(k > 1) adj[i][j][k].push_back({i, j, k - 1}); if(i < n) adj[i][j][k].push_back({i + 1, j, k}); if(j < n) adj[i][j][k].push_back({i, j + 1, k}); if(k < n) adj[i][j][k].push_back({i, j, k + 1}); } } } dijkstra(1, 1, 1); cout << dist[n][n][n] << "\n"; } ``` ::: ## Bài 3: [Số lượng đường đi ngắn nhất](https://marisaoj.com/problem/175) - Với $c_i$ là số lượng đường đi ngắn nhất tới $i$. - Khi cập nhật đường đi ngắn nhất, nếu cập nhật đường đi từ $u$ sang $v$, nếu $d_u+w=d_v$, $f_v$ sẽ tăng thêm $f_u$. Trường hợp $d_u+w<d_v$, gán $f_v=1$ do từ chỉ có duy nhất đường đi ngắn nhất từ $u$ sang $v$. Code: :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; const int N = 2e5 + 5; const long long MOD = 1e9 + 7; typedef pair<long long, int> pli; int n, m; vector<pli> adj[N]; pli d[N]; void dijkstra() { for (int i = 2; i <= n; ++i) { d[i] = {LLONG_MAX, 0}; } d[1] = {0, 1}; priority_queue<pli, vector<pli>, greater<pli>> q; q.push({0, 1}); while (!q.empty()) { long long k = q.top().first; int u = q.top().second; q.pop(); if (d[u].first != k) { continue; } for (pli v: adj[u]) { if (d[v.second].first > k + v.first) { d[v.second] = {k + v.first, d[u].second}; q.push({d[v.second].first, v.second}); } else if (d[v.second].first == k + v.first) { d[v.second].second += d[u].second; } d[v.second].second %= MOD; } } } void fast() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); } int main() { fast(); cin >> n >> m; for (int i = 1; i <= m; ++i) { int u, v; long long w; cin >> u >> v >> w; adj[u].push_back({w, v}); adj[v].push_back({w, u}); } dijkstra(); cout << d[n].second; } ``` ::: ## Bài 4: [Cạnh đôi](https://marisaoj.com/problem/177) - Nhận thấy trọng số cạnh rất bé, ta có trạng thái là $f(i,w)$ là đường đi nhỏ nhất đến $i$ với trọng số cạnh vừa qua là $w$. Do đi 2 cạnh cùng lúc, $w > 0$ khi đã đi qua lẻ cạnh, $w=0$ khi đã đi qua chẵn cạnh. - Từ trạng thái $f(u,w)$ với $w>0$ có thể đến trạng thái $f(v,0)$ với trọng số $w \times w_{u,v}$. - Từ trạng thái $f(u,0)$ có thể đến trạng thái $f(v,w_{u,v})$ với cạnh trọng số $0$. - Ví dụ xuất phát từ $1$ tới $2$ với cạnh trọng số $4$, ta đi từ $f(1, 0) = 0$ đến $f(2, 4) = 0$. Rồi đi từ $2$ tới $3$ với cạnh trọng số $5$ thì đi từ $f(2, 4)$ đến $f(3, 0) = 4 \times 5 = 20$. Code :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; #define ll long long const int inf =1e9; struct Node{ int u,dist; }; struct Edge{ int v,w; }; struct cmp{ bool operator ()(Node a, Node b){ return a.dist > b.dist; } }; vector <Edge> adj[200001]; bool visited[200001]; int n,m; int d[200001]{}; int c=0; void inp(){ cin >> n >> m; for (int i=1;i<=m;i++){ int u,v,w; cin >> u >> v >> w; adj[u].push_back({v,w}); adj[v].push_back({u,w}); } } void dijsktra(){ priority_queue<Node , vector<Node> , cmp> h; queue<int> q; memset(visited , false , sizeof(visited)); h.push({1,0}); for (int i=1;i<=n;i++) d[i]=inf; d[1]=0; while(h.size()){ Node x = h.top(); h.pop(); if (visited[x.u]) continue; visited[x.u]= true; for (Edge k:adj[x.u]) for (Edge e : adj[k.v]){ if (d[e.v] > d[x.u] + e.w *k.w) { d[e.v] = d[x.u] + e.w *k.w; h.push({e.v,d[e.v]}); } } } } void solve(){ inp(); dijsktra(); cout << (d[n]== inf ? -1 : d[n]); } int main(){ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); solve(); } ``` :::