[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;
}
posted @ 2021-08-09 21:18  行舟C  阅读(54)  评论(0)    收藏  举报