ICPC沈阳站M题,对于每一个集合新建一个点,集合里面每个点到这个点的代价是ti,这个点到集合的每个点代价是0,从1和n出发跑两次最短路就行了。
#include <bits/stdc++.h> using namespace std; #define maxn 4111111 #define maxm 21111111 #define INF 11111111111111111 struct node { int to, next; long long w; }edge[maxm]; int n, m, s, u, v, w, head[maxn], cnt; long long d1[maxn], d2[maxn]; void add_edge (int u, int v, long long w, int i) { node &e = edge[i]; e.to = v, e.w = w, e.next = head[u], head[u] = i; } bool vis[maxn]; int top, num[maxn]; bool spfa (int start, long long *d) { memset (vis, 0, sizeof vis); for (int i = 1; i <= n+m; i++) { d[i] = INF; } vis[start] = 1; d[start] = 0; queue <int> q; while (!q.empty ()) q.pop (); q.push (start); memset (num, 0, sizeof num); num[start] = 1; while (!q.empty ()) { int u = q.front (); q.pop (); vis[u] = 0; for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (d[v]>d[u]+edge[i].w) { d[v] = d[u]+edge[i].w; if (!vis[v]) { vis[v] = 1; q.push (v); if (++num[v] > n+m) return 0; } } } } return 1; } int ans_cnt, ans[maxn]; int main () { //freopen ("in", "r", stdin); int t, kase = 0; scanf ("%d", &t); while (t--) { memset (head, -1, sizeof head); cnt = 0; scanf ("%d%d", &n, &m); for (int i = 1; i <= m; i++) { scanf ("%lld%d", &w, &s); for (int j = 1; j <= s; j++) { scanf ("%d", &u); add_edge (n+i, u, w, cnt++); add_edge (u, n+i, 0, cnt++); } } spfa (1, d1); spfa (n, d2); long long Min = INF; ans_cnt = 0; for (int i = 1; i <= n; i++) { long long cur = max (d1[i], d2[i]); if (cur < Min) { Min = cur; ans_cnt = 1; ans[ans_cnt] = i; } else if (cur == Min) { ans_cnt++; ans[ans_cnt] = i; } } printf ("Case #%d: ", ++kase); if (Min == INF) { printf ("Evil John\n"); } else { printf ("%lld\n", Min); for (int i = 1; i < ans_cnt; i++) printf ("%d ", ans[i]); printf ("%d\n", ans[ans_cnt]); } } return 0; }