原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=5521
题意:给一些集合,集合里的任意两点可以花费某一时间
t
i
ti
ti到达
有两人分别在
1
1
1号点和
n
n
n号点
两人同时出发,问在哪一点见面花费的时间最短
输出最短时间和见面的点(如果有多个点从小到大输出多个)
思路:因为集合太大,不可以把每个集合的所有点建边,我们引入m个虚拟的点,每个集合的点连接引入的点,然后集合的点向虚拟的点建一条正常权值的边,再建一条反向边,权值为0.这样子建边的复杂度从
n
2
n^2
n2变成了
2
n
2n
2n.
然后只要跑正反两遍dij,然后遍历寻找一个最优的点就行了.
参考博客:https://blog.csdn.net/Code92007/article/details/82795407
#include <bits/stdc++.h>
#define eps 1e-8
#define INF 0x3f3f3f3f3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)+1
#define CLR(x,y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int seed = 131;
const int maxn = 30e5 + 5;
const int mod = 1e9 + 7;
struct node {
int v, w, nxt;
node() {}
node(int v, int w): v(v), w(w) {}
bool operator <(const node &a)const {
return w > a.w;
}
} e[maxn];
int tot, head[maxn];
void add_edge(int u, int v, int w) {
e[tot].v = v;
e[tot].w = w;
e[tot].nxt = head[u];
head[u] = tot++;
}
int T, n, m;
int a[maxn];
bool vis[maxn];
ll dis[3][maxn];
int DIS[maxn];
void dij(int start, int id) {
for (int i = 0; i <= n + m; i++) {
dis[id][i] = INF;
vis[i] = 0;
}
priority_queue<node>q;
dis[id][start] = 0;
q.push(node(start, dis[id][start]));
while (!q.empty()) {
int u = q.top().v;
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if (dis[id][v] > dis[id][u] + e[i].w) {
dis[id][v] = dis[id][u] + e[i].w;
q.push(node(v, dis[id][v]));
}
}
}
}
int main() {
scanf("%d", &T);
for (int cas = 1; cas <= T; cas++) {
CLR(head, -1);
tot = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int val, num;
scanf("%d%d", &val, &num);
for (int j = 1; j <= num; j++) {
scanf("%d", &a[j]);
add_edge(a[j], n +i, val);
add_edge(n + i, a[j], 0);
}
}
dij(1, 1);
dij(n, 2);
ll MIN = INF;
vector<ll>v;
v.clear();
for (int i = 1; i <= n; i++) {
ll maxx = max(dis[1][i], dis[2][i]);
if (maxx < MIN) {
MIN = maxx;
v.clear();
v.push_back(i);
} else if (maxx == MIN) {
v.push_back(i);
}
}
printf("Case #%d: ", cas);
if (MIN == INF) printf("Evil John\n");
else {
printf("%d\n", MIN);
int cnt=v.size();
for (int i = 0; i < cnt; i++) {
printf("%d%c", v[i], i == cnt - 1 ? '\n' : ' ');
}
}
}
return 0;
}