题目大意:两头牛在图的1和n处,现给定一些区域集合Ei,这些区域集合中的点是可以互相到达的,且时间花费都为Ti,问两头牛能否相遇,相遇的最短时间和相遇的点的标记。
题解报告:当初想到跑两边dijkstra,然而脑子抽不知道怎么保存和输出相遇的点的路径,结果就全盘否定,最后也没写多少。。。。。还是做题做少了。。。
思路:这题就是dijkstra最短路,不过建边时候要考虑一个问题,这个题的点为1e5个,那么用邻接表,边会达到1e10个,最短路会超时,所以这时候我们将一个集合加为一个点,点的编号从n+1开始,再将集合中所有的点与这个大点建边,权值为时间Ti,反向建边时权值为0(为什么为0自己画个图就知道了)。这样我们新加了1e5个点,而又因为Si的和不超过1e6,所以我们最后边的总数最大为2e6,这样就不会超时了,也可以开的下数组。开数组大小等细节详细看代码吧~
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
/*
dijkstra*2,枚举最短路的每个节点
注意建边的技巧,这题n为1e5,不能直接建边,否则超时
将每个s后的s个边与一个源点建立s条边,权值为t表示时间
反向建边时权值为0
这样总边数为2*1e6
这有着网络流的建边思想
*/
typedef long long ll;
const ll INF=0x3f3f3f3f;
const int maxn=1e5+10;//点
const int maxm=2e6+10;//边
struct Edge
{
int from;
int to;
int next;
ll dist;
}edges[maxm];
struct HeapNode
{
ll d;
int u;
HeapNode(ll _d,int _u):d(_d),u(_u){}
bool operator < (const HeapNode &rhs)const
{
return d>rhs.d;
}
};
int head[maxn+maxm],tot;//节点数多增加1e6个
int n,m;
int vis[maxn+maxm];
ll dist_x[maxn+maxm],dist_y[maxn+maxm];
void init()
{
memset(head,-1,sizeof(head));
tot=0;
}
void add_edges(int u,int v,ll dist)
{
edges[tot].from=u;
edges[tot].to=v;
edges[tot].dist=dist;
edges[tot].next=head[u];
head[u]=tot++;
}
void dijkstra(int s,ll *d)//求点s到每一个点的最短距离,存入d数组中
{
priority_queue<HeapNode>Q;
for(int i=0;i<=n+m+1;i++)d[i]=INF;
d[s]=0;
memset(vis,0,sizeof(vis));
//memset(visNode,0,sizeof(visNode));
Q.push(HeapNode(0,s));
while(!Q.empty())
{
HeapNode N=Q.top();Q.pop();
int u=N.u;
if(vis[u])continue;
vis[u]=true;
for(int i=head[u];i!=-1;i=edges[i].next)
{
int v=edges[i].to;
if(d[v]>d[u]+edges[i].dist)
{
d[v]=d[u]+edges[i].dist;
Q.push(HeapNode(d[v],v));
}
}
}
}
int main()
{
int T;
scanf("%d",&T);
int Case=1;
while(T--)
{
scanf("%d%d",&n,&m);
init();
ll time;
int si;
for(int i=1;i<=m;i++)
{
scanf("%lld%d",&time,&si);
int index=n+i;//多增节点从n+1开始编号
for(int j=1;j<=si;j++)
{
int num;
scanf("%d",&num);
add_edges(num,index,time);
add_edges(index,num,0);//为什么是0呢?这样保证连接同一个s的距离只有T;
}
}
dijkstra(1,dist_x);
dijkstra(n,dist_y);
ll ans=INF;
for(int i=1;i<=n;i++)
{
ans=min(ans,max(dist_x[i],dist_y[i]));//因为可以等待,所以取两者到每一个点的最大值
}
if(ans==INF)
{
printf("Case #%d: Evil John\n",Case++);
}
else
{
printf("Case #%d: %lld\n",Case++,ans);
bool flag=false;
for(int i=1;i<=n;i++)
{
if(ans==max(dist_x[i],dist_y[i]))
{
if(flag)printf(" ");
printf("%d",i);
flag=true;
}
}
printf("\n");
}
}
return 0;
}