题意:共有N个点,每si个点为一个集合,集合内部任意两点的路程所需时间为ti, 两个人分别住在点1和点N,问两人从自己住的地方,找一个点相遇,要求两人用最短的时间相遇。
思路:看到这个题有点蒙,因为si中任意两点都有边,如果要表示的话相当于要建好多个完全图,而数据范围一看,肯定的爆内存。后来看到了别人写的代码,发现并不需要建立完全图,对于每个集合,我们都给它新加一个顶点,建立集合内顶点到新加顶点的边,这样就可以解决爆内存的问题了。剩下的就是一个简单的最短路了。试了一下, SPFA和dijkstra都能过, 不过堆优化的dijkstra要快一点。
代码:
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
const int N = 100005;
const int M = 1000005;
const int INF = 0x3f3f3f3f;
int n, m;
typedef long long LL;
struct Edge {
int v, w, next;
};
struct Node {
int v, w;
Node() {};
Node(int pv, int pw):v(pv), w(pw) {};
bool operator < (const Node& other)const {
return w > other.w;
}
};
Edge edge[(N+M)<<1];
int fir[N+M];
int dis[N+M][2];
int cnt;
void init() {
cnt = 0;
memset(fir, -1, sizeof(fir));
}
void addEdge(int u, int v, int w) {
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = fir[u];
fir[u] = cnt++;
}
void dijkstra(int s, int k) {
priority_queue<Node> pque;
dis[s][k] = 0;
pque.push(Node(s, 0));
while(!pque.empty()) {
Node cur = pque.top();
pque.pop();
for(int i = fir[cur.v]; i != -1; i = edge[i].next) {
int v = edge[i].v;
if(dis[cur.v][k] + edge[i].w < dis[v][k]) {
dis[v][k] = dis[cur.v][k] + edge[i].w;
pque.push(Node(v, dis[v][k]));
}
}
}
}
int main () {
int t;
scanf("%d", &t);
for(int cas = 1; cas <= t; cas++) {
scanf("%d%d", &n, &m);
init();
for(int i = 1; i <= m; i++) {
int ti, si;
scanf("%d%d", &ti, &si);
while(si--) {
int id;
scanf("%d", &id);
addEdge(id, i + n, ti);
addEdge(i + n, id, 0);
}
}
memset(dis, INF, sizeof(dis));
dijkstra(1, 0);
dijkstra(n, 1);
int ans = INF;
for(int i = 1; i <= n; i++) {
if(dis[i][0] != INF && dis[i][1] != INF) {
ans = min(ans, max(dis[i][0], dis[i][1]));
}
}
if(ans == INF)
printf("Case #%d: Evil John\n", cas);
else {
printf("Case #%d: %d\n", cas, ans);
int flag = 1;
for(int i = 1; i <= n; i++) {
int tmp = max(dis[i][0], dis[i][1]);
if(tmp == ans) {
if(flag) {
flag = 0;
printf("%d", i);
} else {
printf(" %d", i);
}
}
}
printf("\n");
}
}
return 0;
}