---
# System prepended metadata

title: Chữa bài dijkstra 08/08/2023

---

# 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();
}
```
:::