题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5521
学习菊苣的博客,只粘链接,不粘题目描述了。
题目大意就是一个人从1开始走,一个人从n开始走。让最后相遇的时间最短。
题目就是个最短路,不过唯一不同的是,题目图的描述方式比较特别。
从规模上来看,想把这张图描述成邻接矩阵或者邻接表是不可能的。
必然只能按照题目要求的意思来存。
于是第一步存图的方式,我采用了两个vector数组,(当然此处可以使用链式前向星),一个存了和点相关的集合有哪些in[],即这个点包含在哪些集合里面;一个存了集合里面有哪些点edge[]。
然后就是跑两遍最短路了,这里我采用了优先队列的spfa(貌似就是优先队列的dijikstra)。
不过直接这样上去T了好多发。
然后考虑到用了优先队列后,相当于优化了这个dp的过程,让每个点只需要遍历一次。那么,自然而然的,如果i遍历了集合s里面的所有点,自然集合s也只需要遍历一次。加了这一个剪枝就能AC了。由于遍历集合的时候,不是按照从最小权值集合开始的,所以这里没有对队列里面的元素进行判重,不过影响不是很大。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <set> #include <queue> #include <vector> #define LL long long using namespace std; struct node { int id, val; bool operator<(const node x) const { return val>x.val; } }; const int maxN = 100005; const int maxM = 1000005; int n, m; vector<int> in[maxN]; int dis[maxM]; vector<int> edge[maxM]; LL p[2][maxN]; bool vis[maxM]; void input() { scanf("%d%d", &n, &m); int t, s, k; for (int i = 0; i < m; ++i) { scanf("%d%d", &t, &s); dis[i] = t; for (int j = 0; j < s; ++j) { scanf("%d", &k); in[k].push_back(i); edge[i].push_back(k); } } memset(p, -1, sizeof(p)); } void spfa(int from, int x) { memset(vis, false, sizeof(vis)); priority_queue<node> q; node k, t; int w, u, v; k.id = from; k.val = 0; q.push(k); p[x][from] = 0; while (!q.empty()) { k = q.top(); q.pop(); u = k.id; for (int i = 0; i < in[u].size(); ++i) { w = in[u][i]; if (vis[w]) continue; for (int j = 0; j < edge[w].size(); ++j) { v = edge[w][j]; if (u == v) continue; if (p[x][v] == -1 || p[x][v] > p[x][u]+dis[w]) { p[x][v] = p[x][u]+dis[w]; t.id = v; t.val = p[x][v]; q.push(t); } } vis[w] = true; } } } LL myMin(LL x, LL y) { if (x == -1) return y; else return min(x, y); } void work() { spfa(1, 0); spfa(n, 1); LL mi = -1; for (int i = 1; i <= n; ++i) if (p[0][i] != -1 && p[1][i] != -1) mi = myMin(mi, max(p[0][i], p[1][i])); if (mi == -1) { printf("Evil John "); return; } printf("%d ", mi); bool flag = false; for (int i = 1; i <= n; ++i) if (p[0][i] != -1 && p[1][i] != -1 && mi == max(p[0][i], p[1][i])) { if (flag) printf(" "); printf("%d", i); flag = true; } printf(" "); } void clss() { for (int i = 0; i < m; ++i) edge[i].clear(); for (int i = 1; i <= n; ++i) in[i].clear(); } int main() { //freopen("test.in", "r", stdin); int T; scanf("%d", &T); for (int times = 0; times < T; ++times) { printf("Case #%d: ", times+1); input(); work(); clss(); } return 0; }