题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5521
题意;
给你一副图,n个节点,一个人在1,一个人在n问你在哪个节点相遇花的时间最短。
题解:
比较快想到的思路就是起点,终点都跑一遍最短路。
但是发现边太多,图建不出来。
重新构造一幅等价的图可以解决这道题:
为每一个集合建一个新节点,属于某个集合的节点,建一条权值为0的边到代表这个集合的新节点,同时新节点向属于这个集合的节点建一条权值为w(w为这个集合任意两点的距离)的边。
然后跑两次最短路。
证明:
属于集合的点到集合的距离自然为0,而集合到点的距离则表示着从该集合的另一个点到达这个点,所以距离为w。
#include<iostream> #include<cstdio> #include<vector> #include<algorithm> #include<cstring> #include<queue> #define X first #define Y second #define mp make_pair using namespace std; const int maxn = 2e5 + 10; const int INF = 0x3f3f3f3f; int n, m; vector<pair<int,int> > G[maxn]; int d1[maxn], d2[maxn],dd[maxn]; int inq[maxn]; int ans; void spfa(int s,int *d) { memset(inq, 0, sizeof(inq)); memset(d, 0x7f, sizeof(int)*maxn); queue<int> Q; Q.push(s), d[s] = 0, inq[s] = 1; while (!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = 0; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i].X, w = G[u][i].Y; if (d[v] > d[u] + w) { d[v] = d[u] + w; if (!inq[v]) { inq[v] = 1, Q.push(v); } } } } } void init() { ans = INF; for (int i = 0; i < n + m; i++) G[i].clear(); } int main() { int tc,kase=0; scanf("%d", &tc); while (tc--) { scanf("%d%d", &n, &m); init(); for (int i = 0; i < m; i++) { int w, cnt,v; scanf("%d%d", &w, &cnt); for (int j = 0; j < cnt; j++) { scanf("%d", &v); v--; G[i + n].push_back(mp(v, w)); G[v].push_back(mp(i + n, 0)); } } spfa(0, d1); spfa(n - 1, d2); for (int i = 0; i < n; i++) { dd[i] = max(d1[i], d2[i]); ans = min(ans, dd[i]); } vector<int> vec; for (int i = 0; i < n; i++) if (dd[i] == ans) { vec.push_back(i + 1); } printf("Case #%d: ", ++kase); if (ans == INF) { printf("Evil John "); } else { printf("%d ", ans); for (int i = 0; i < vec.size() - 1; i++) printf("%d ", vec[i]); printf("%d ", vec[vec.size() - 1]); } } return 0; }
还有一种思路是用dijkstra做,因为每个集合都只需要做第一次访问(一定是从离起点最近的路径访问进来的,之后访问该集合的都不可能通过集合内的边来松弛了)的松弛操作。
所以不会超时。
#include<iostream> #include<cstdio> #include<vector> #include<queue> #include<utility> #include<cstring> using namespace std; const int maxn = 1e5 + 10; const int INF = 0x3f3f3f3f; struct Edge { int v, d; Edge(int v, int d) :v(v), d(d) {} Edge() {} bool operator < (const Edge& tmp) const { return d > tmp.d; } }; vector<int> G[maxn], S[maxn]; int val[maxn], n, m; int d1[maxn], d2[maxn],dd[maxn], used[maxn], vis[maxn]; void dij(int s, int *d) { memset(d, 0x7f, sizeof(int)*maxn); memset(used, 0, sizeof(used)); memset(vis, 0, sizeof(vis)); priority_queue<Edge> pq; pq.push(Edge(s, 0)),d[s] = 0; while (!pq.empty()) { int u = pq.top().v; pq.pop(); if (used[u]) continue; used[u] = 1; for (int i = 0; i < G[u].size(); i++) { int s = G[u][i]; if (vis[s]) continue; vis[s] = 1; for (int j = 0; j < S[s].size(); j++) { int v = S[s][j]; if (v == u) continue; if (d[v] > d[u] + val[s]) { d[v] = d[u] + val[s]; pq.push(Edge(v, d[v])); } } } } } void init() { for (int i = 0; i < n; i++) G[i].clear(); for (int i = 0; i < m; i++) S[i].clear(); } int main() { int tc,kase=0; scanf("%d", &tc); while (tc--) { scanf("%d%d", &n, &m); init(); for (int i = 0; i < m; i++) { int cnt, v; scanf("%d%d", &val[i], &cnt); for (int j = 0; j < cnt; j++) { scanf("%d", &v), v--; G[v].push_back(i); S[i].push_back(v); } } dij(0, d1); dij(n - 1, d2); int ans = INF; for (int i = 0; i < n; i++) { dd[i] = max(d1[i], d2[i]); ans = min(ans, dd[i]); } printf("Case #%d: ", ++kase); if (ans == INF) { printf("Evil John "); } else { printf("%d ", ans); vector<int> a; for (int i = 0; i < n; i++) { if (dd[i] == ans) { a.push_back(i + 1); } } for (int i = 0; i < a.size() - 1; i++) printf("%d ", a[i]); printf("%d ", a[a.size() - 1]); } } return 0; }