洛谷 P1078 [NOIP2012 普及组] 文化之旅

题意

有一个 n n n m m m 边的无向图,每个点都有一个颜色(可能重复),还给定了一个矩阵 A A A。如果经过了颜色为 i i i 的点,那就不能再经过颜色为 i i i 的点以及满足 A i , j = 1 A_{i,j}=1 Ai,j=1 的颜色为 j j j 的点。求 s → t s\to t st 的最短路,不保证 A i , j = A j , i A_{i,j}=A_{j,i} Ai,j=Aj,i

思路

由于 A i , j = A j , i A_{i,j}=A_{j,i} Ai,j=Aj,i 不一定成立,所以不能用并查集维护。考虑到数据范围较小,可以用暴力的做法,记录下路径,在 dijkstra 松弛下一个点前,判断这个点的颜色是否和走过的冲突,有冲突就忽略。

代码

// Problem: P1078 [NOIP2012 普及组] 文化之旅
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1078
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <iostream>
#include <queue>
using namespace std;
#define int long long
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n, k, m, s, t;
	cin >> n >> k >> m >> s >> t;
	s--, t--;
	
	vector<int> c(n);
	for (auto &i: c) {
		cin >> i;
		i--;
	}
	
	vector<vector<int>> a(k, vector<int>(k));
	for (auto &i: a)
		for (auto &j: i) cin >> j;
		
	vector<vector<PII>> G(n);
	for (int i = 0, u, v, w; i < m; i++) {
		cin >> u >> v >> w;
		u--, v--;
		G[u].push_back({v, w});
		G[v].push_back({u, w});
	}
	
	vector<int> dis(n, INF), pre(n, -1);
	vector<bool> vis(n, false);
	
	auto check = [&](int u, int v) {
		int tmp = u;
		while(tmp != -1){
			if(a[c[v]][c[tmp]] || c[v] == c[tmp]) return false;
			tmp = pre[tmp];
		}
		return true;
	};
	
	auto dij = [&](int s) {
		priority_queue<PII, vector<PII>, greater<PII>> q;
		
		dis[s] = 0;
		q.push({0, s});
		
		while (q.size()) {
			int u = q.top().second;
			q.pop();
			
			if (vis[u]) continue;
			vis[u] = true;
			for (auto &edge: G[u]) {
				int v = edge.first, w = edge.second;
				if(dis[v] > dis[u] + w && check(u, v)){
					dis[v] = dis[u] + w;
					pre[v] = u;
					q.push({dis[v], v});
				}
			}
		}
	};
	
	dij(s);
	cout << (dis[t] == INF? -1: dis[t]) << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值