题意:
有(n)个点和(m)个点的集合(E_i),每个集合中的点两两相邻而且通过任意两点之间需要的时间为(t_i)。
有两个人分别从(1)到(n)出发,确定一个相遇点使得两个人相遇的时间最短。
分析:
起初想法是跑两遍最短路,分别计算(1)和(n)为起点时的最短路(ds(i))和(dt(i))。
那么所求的最短时间为(min\{ max(ds(i), dt(i)) \})。
但是边的数量太多了,传统的建图方法行不通。
考虑题目所给的条件,在途中新增(m)个点代表这些集合。
对于一个点(u)和它所属的集合(S),建一条(u
ightarrow S)权值为(0)的边,和一条(S
ightarrow u)的权值为(w)的边。
然后求最短路就是答案。
说一下我对这种见图方法的理解:
- 边(u ightarrow S)表示从点(u)进入集合,因为点本来就在集合中所以不需要花费。
- 边(S ightarrow 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
"); continue; }
Dijkstra(dt, n);
#ifdef DEBUG
printf("ds: ");
for(int i = 1; i <= n; i++) printf("%d ", ds[i]);
printf("
dt: ");
for(int i = 1; i <= n; i++) printf("%d ", dt[i]);
printf("
");
#endif
int ans = INF;
for(int i = 1; i <= n; i++) ans = min(ans, max(ds[i], dt[i]));
printf("%d
", 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("
");
}
return 0;
}