NC 追債之旅 (dijkstra+分層圖)
阿新 • • 發佈:2020-08-07
題意:給一張n個節點,m條雙向邊的圖,每條邊具有花費,每一天走一條邊,每一天也具有花費。
問小明從1號結點到n號結點k天之內能否到達,能到達輸出最小花費,否則輸出-1。
解法:一開始沒有想到建分層圖,直接dijkstra貪心走最小花費,但是會出現一個問題,就是
到達一個結點的最小花費,會被其他路徑天數並不匹配的路徑所利用,從而得到答案錯誤。
hack資料:
4 4 2
1 2 1
1 3 1000
2 3 5
3 4 4999
0 1
如果沒建分層圖,1-2-3的最短花費先被更新為7,1-3-4這條路徑中3-4這一次更新會利用1-2-3的最小花費更新到4的路徑,導致答案錯誤。
所以為了避免這種天數不匹配的情況,需要分層建圖,或則dis陣列多開一維記錄資訊。dis[i][j]表示到達i結點路徑長為j的最小花費
這樣轉移就不會出現天數不匹配的情況。
#include <bits/stdc++.h> typedef long long ll ; //#define int ll int quickpow(int a , int b , int mo){int ans = 1 ;while(b){ if(b&1){ans = ans * a % mo ;}b >>= 1 ;a = a * a % mo ;}return ans ;} using namespace std ; #define INF 0x3f3f3f3f const double PI = acos(-1.0); const int maxn = 1e4+9; const int mod = 1e9+7; int n , m , k ; int dis[maxn][20]; bool vis[maxn][20]; int da[maxn]; vector<pair<int , int>>g[maxn]; struct node{ int u , w , t ; bool operator < (const node e) const{ return w > e.w; } node(int _u , int _w , int _t){ u = _u , w = _w , t = _t ; } }; void dijkstra(int u){ priority_queue<node>q; for(int i = 1 ; i <= n ; i++){ for(int j = 0 ; j <= k ; j++){ dis[i][j] = INF; vis[i][j] = false; } } dis[u][0] = 0 ; q.push(node{u , dis[u][0] , 0}); while(!q.empty()){ node now = q.top() ; q.pop(); int u = now.u , nt = now.t; vis[u][nt] = 1; if(nt >= k) continue; for(auto j : g[u]){ int v = j.first , w = j.second , t = nt + 1; if(!vis[v][t] && dis[v][t] > dis[u][nt] + w + da[t]){ dis[v][t] = dis[u][nt] + w + da[t]; q.push(node{v , dis[v][t] , t}); } } } } void Solve(){ cin >> n >> m >> k ; for(int i = 1 ; i <= m ; i++){ int u , v , w ; cin >> u >> v >> w ; g[u].push_back({v , w}); g[v].push_back({u , w}); } for(int i = 1 ; i <= k ; i++){ cin >> da[i]; } dijkstra(1); int ans = INF; for(int i = 1 ; i <= k ; i++){ ans = min(ans , dis[n][i]); } cout << (ans == INF ? -1 : ans) << endl; } signed main(){ #ifdef ONLINE_JUDGE #else freopen("D:\\c++\\in.txt", "r", stdin); #endif Solve(); }