1. 程式人生 > 其它 >[NC22594]Rinne Loves Graph

[NC22594]Rinne Loves Graph

一、題目

NC22594

二、思路

這題的題意很明顯,就是在走到的點如果是有看守的點,那麼從這個點走到下個點的時候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;
}