[NC22594]Rinne Loves Graph
阿新 • • 發佈:2021-08-09
一、題目
二、思路
這題的題意很明顯,就是在走到的點如果是有看守的點,那麼從這個點走到下個點的時候k就要+1,求經過的看守點不超過k次的最短路徑
這題一看到就能想到要用分層圖來寫,因為有個類似天數的限制,然後看了題解後發現也能用最短路dp來做,但是最短路dp我還沒學明白,等以後有機會再回來補充下這題的最短路dp的寫法
建圖的過程就是如果當前點是看守點,那麼就要和下一層的下一個點來連線,反之亦然,如果當前點不是看守點,那直接在當前層和下一個點連邊即可
連邊的時候注意一下層的範圍限制,詳細看程式碼
三、程式碼
#include<bits/stdc++.h> using namespace std; const int N = 10010 * 10; //因為是分層圖,記得乘層數 typedef pair<int, int> PII; int n, m, k; int h[N], e[N], ne[N], w[N], idx; int dist[N]; bool st[N]; int f[N]; void add(int a, int b, int c){ w[idx] = c; e[idx] = b; ne[idx] = h[a]; h[a] = idx ++; } void dijkstra(){ //堆優化版的dijkstra板子 memset(dist, 0x3f, sizeof dist); dist[1] = 0; priority_queue<PII, vector<PII>, greater<PII> > heap; heap.push({0, 1}); while(heap.size()){ PII k = heap.top(); heap.pop(); int ver = k.second, distance = k.first; if(st[ver]) continue; st[ver] = true; for(int i = h[ver]; i != -1; i = ne[i]){ int j = e[i]; if(dist[j] > distance + w[i]){ dist[j] = distance + w[i]; heap.push({dist[j], j}); } } } } int main(){ cin >> n >> m >> k; memset(h, -1, sizeof h); for(int i = 1; i <= n; i ++){ int t; cin >> t; f[i] = t; } int u, v, w; for(int i = 1 ; i <= m; i ++){ cin >> u >> v >> w; if(!f[u]){ //這裡是當前點不是看守點的時候,每層直接連邊即可 for(int j = 0; j <= k; j ++) add(u + j * n, v + j * n, w); } if(!f[v]){ for(int j = 0; j <= k; j ++) add(v + j * n, u + j * n, w); } if(f[u]){ //這裡是當前點為看守點的時候,要和下一層的下一個點連邊,注意j的範圍是0 ~ k - 1, 因為下一個點最大可以到j + 1層 for(int j = 0; j < k; j ++) add(u + j * n, v + (j + 1) * n, w); } if(f[v]){ for(int j = 0; j < k; j ++) add(v + j * n, u + (j + 1) * n, w); } } dijkstra(); int ans = 0x3f3f3f3f; //這裡要記得別打0x3f。。。我memset的時候打習慣了,要打0x3f3f3f3f,不然會wa for(int i = 1; i <= k + 1; i ++){ ans = min(ans, dist[i * n]); //n的取值範圍是第1層的n一直到第k + 1層的n } if(ans == 0x3f3f3f3f) cout << "-1" << endl; else cout << ans << endl; return 0; }