题意
有一个 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 s→t 的最短路,不保证 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;
}