题意:
有\(n\)个点和\(m\)个点的集合\(E_i\),每个集合中的点两两相邻而且通过任意两点之间需要的时间为\(t_i\)。
有两个人分别从\(1\)到\(n\)出发,确定一个相遇点使得两个人相遇的时间最短。
分析:
起初想法是跑两遍最短路,分别计算\(1\)和\(n\)为起点时的最短路\(ds(i)\)和\(dt(i)\)。
那么所求的最短时间为\(min\\\{ max(ds(i), dt(i)) \\\}\)。
但是边的数量太多了,传统的建图方法行不通。
考虑题目所给的条件,在途中新增\(m\)个点代表这些集合。
对于一个点\(u\)和它所属的集合\(S\),建一条\(u \rightarrow S\)权值为\(0\)的边,和一条\(S \rightarrow u\)的权值为\(w\)的边。
然后求最短路就是答案。
说一下我对这种见图方法的理解:
- 边\(u \rightarrow S\)表示从点\(u\)进入集合,因为点本来就在集合中所以不需要花费。
- 边\(S \rightarrow u\)表示在集合中从一个点移动到另一个点,花费为\(w\)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#define MP make_pair
using namespace std;
typedef pair<int, int> PII;
const int maxn = 200000 + 10;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u, v, d;
Edge() {}
Edge(int u, int v, int d):u(u), v(v), d(d) {}
};
int n, m;
vector<int> G[maxn];
vector<Edge> edges;
void init(int n) {
for(int i = 1; i <= n; i++) G[i].clear();
edges.clear();
}
void AddEdge(int u, int v, int d) {
edges.push_back(Edge(u, v, d));
edges.push_back(Edge(v, u, 0));
int m = edges.size();
G[u].push_back(m - 2);
G[v].push_back(m - 1);
}
int ds[maxn], dt[maxn];
bool vis[maxn];
void Dijkstra(int* d, int s) {
memset(d, 0x3f, sizeof(ds));
memset(vis, false, sizeof(vis));
d[s] = 0;
priority_queue<PII, vector<PII>, greater<PII> > Q;
Q.push(MP(0, s));
while(!Q.empty()) {
PII x = Q.top(); Q.pop();
int u = x.second;
if(vis[u]) continue;
vis[u] = true;
for(int i = 0; i < G[u].size(); i++) {
Edge& e = edges[G[u][i]];
int v = e.v;
if(d[u] + e.d < d[v]) {
d[v] = d[u] + e.d;
Q.push(MP(d[v], v));
}
}
}
}
int main()
{
int T; scanf("%d", &T);
for(int kase = 1; kase <= T; kase++) {
scanf("%d%d", &n, &m);
init(n + m);
for(int i = 1; i <= m; i++) {
int d, cnt; scanf("%d%d", &d, &cnt);
while(cnt--) {
int u; scanf("%d", &u);
AddEdge(n + i, u, d);
}
}
printf("Case #%d: ", kase);
Dijkstra(ds, 1);
if(ds[n] == INF) { printf("Evil John\n"); continue; }
Dijkstra(dt, n);
#ifdef DEBUG
printf("ds: ");
for(int i = 1; i <= n; i++) printf("%d ", ds[i]);
printf("\ndt: ");
for(int i = 1; i <= n; i++) printf("%d ", dt[i]);
printf("\n");
#endif
int ans = INF;
for(int i = 1; i <= n; i++) ans = min(ans, max(ds[i], dt[i]));
printf("%d\n", ans);
bool flag = false;
for(int i = 1; i <= n; i++) {
if(max(ds[i], dt[i]) == ans) {
if(flag) printf(" ");
printf("%d", i); flag = true;
}
}
printf("\n");
}
return 0;
}