题目大意:有N个点,两个人,其中一个人住在点1,另一个人住在点n 。有M个点集,集合内的数表示任意两点的距离为dis ,现在问,如果两个人要见面,需要最短距离是多少,有哪几个点能被当成见面点
解题思路:分别对两个点进行最短路,但是不能直接最短路,这里有一个问题是,边太多了 ,其实也不会很多,每个点集只能被松弛一次,因为里面所有的点已经是最短的状态了,存图的时候注意,存一下每个点所关联的集合,以及每个集合包含的边,当需要松弛u点时,先找到与i相关的每个集合,如果该集合已经被更新过了,那就不需要对其进行更新了,若没更新,对集合里的所有点进行松弛更新
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;///点的个数
const int maxm = 1e6+100;///边的个数
typedef long long ll;
const ll INF = 0xfffffff;
int vis[maxn],flag[maxn];///vis表示点是否访问过,flag表示集合是否访问过
ll tt[maxm],dis1[maxn],dis2[maxn];
int n,m;///n个点,m个集合
vector<ll> G[maxn],E[maxm];///G[i]存存在i结点的所有集合,E[i]存结合i里面的所有结点
typedef pair<ll,int> pii;
void Dijkstra(int s,ll *dis)
{
memset(flag,0,sizeof(flag));
memset(vis,0,sizeof(vis));
for(int i = 1 ; i <= n ; i++) ///初始化点的距离
dis[i] = INF;
dis[s] = 0;
priority_queue<pii,vector<pii>,greater<pii> > pq;
pq.push(make_pair(dis[s],s));
while(!pq.empty())
{
pii tmp = pq.top();
pq.pop();
int u = tmp.second; ///队首节点
if(vis[u]) continue;
vis[u] = 1;
for(int i = 0; i < G[u].size(); i++)///对个与队首结点相连的每个结点进行举例更新
{
int k = G[u][i];///该结点在第k集合
if(flag[k]) continue;
flag[k] = 1;///k集合
for(int j = 0; j < E[k].size(); j++)
{
int v = E[k][j];///对于k集合中的结点v
if(dis[v] > dis[u]+tt[k])
{
dis[v] = dis[u]+tt[k];
pq.push(make_pair(dis[v],v));
}
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
int cas = 1;
while(t--)
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
G[i].clear();
for(int i = 1; i <= m; i++)
{
E[i].clear();
int x,y;///i集合有x个点
scanf("%lld%d",&tt[i],&x);
for(int j = 1; j <= x; j++)
{
scanf("%d",&y);
G[y].push_back(i);
E[i].push_back(y);
}
}
Dijkstra(1,dis1);
Dijkstra(n,dis2);
vector<int> v1;
ll ans = INF;
for(int i = 1; i <= n; i++)
{
ll tmp = max(dis1[i],dis2[i]);
if(tmp < ans)
{
ans = tmp;
v1.clear();
}
if(ans == tmp)
v1.push_back(i);
}
if(ans == INF)
{
printf("Case #%d: Evil John\n",cas++);
continue;
}
printf("Case #%d: %lld\n",cas++,ans);
for(int i = 0; i < v1.size(); i++)
printf("%d%c",v1[i],i==v1.size()-1 ? '\n' : ' ');
}
return 0;
}