D88 最短路+虚点建图 Dijkstra 算法 HDU 5521 Meeting
D88 最短路+虚点建图 Dijkstra 算法 HDU 5521 Meeting_哔哩哔哩_bilibili
n 个点被划分到 m 个集合里面,同一个集合里面的任意两点之间移动 花费时间 t 均相等
每个集合给的数据:t,s,v1,v2, ..., vs
求两人分别从 1 和 n 同时出发,最短时间能在哪些点上会面,输出最短时间和会面的点
思路
会面,当然是分别从 1 号点和 n 号点跑一遍 Dijkstra,然后取 $min(max(d1[i],d2[i]))$
难点是建图,如果集合内的点两两之间连边,规模达到 $O(n^2)$,会爆
发现“同一个集合里面的任意两点之间移动 花费时间 t 均相等”,那么,每个集合建一个虚点,该集合内的点均与该虚点连边,
连边的边权均取 $t/2$,虚点就成了集合内各点的“媒婆”——牵线搭桥,这样连边的数量就降为 $O(m)$

样例模拟:
方案一:男从 1 号出发,经 1 秒到 3 号;女从 5 号出发,经 3 秒到 3 号,所以用时 3 秒,会面地点 3 号
方案二:男从 1 号出发,经 1 秒到 3 号,再经 2 秒到 4 号;女从 5 号出发,经 3 秒到 4 号,所以用时 3 秒,会面地点 4 号

集合数 $m=\sum S_i/2=5e5$,点数 $N=n+m=6e5$,边数 $M=2*m*2=2e6$
小技巧:与虚点连边时边权取 $t$,可避免小数,对结果取 $res/2$ 输出
// 最短路+虚点建图 Dijkstra 算法 O(MlogN) #include<bits/stdc++.h> #define INF 0x3f3f3f3f #define pii pair<int,int> using namespace std; const int N=6e5+10; //点数 vector<pii> e[N]; int n,m; int d1[N],d2[N]; void dijkstra(int s,int d[]){ memset(d,0x3f,sizeof d1); d[s]=0; priority_queue<pii,vector<pii>,greater<pii> > q; q.emplace(0,s); while(!q.empty()){ auto [dd,u]=q.top(); q.pop(); if(dd!=d[u]) continue; for(auto [v,w]:e[u]){ if(d[v]>d[u]+w){ d[v]=d[u]+w; q.emplace(d[v],v); } } } } int main(){ int T; scanf("%d",&T); for(int cas=1; cas<=T; ++cas){ scanf("%d %d",&n,&m); for(int i=0;i<=n+m;++i) e[i].clear(); for(int i=1,t,s,v;i<=m;++i){ scanf("%d %d",&t,&s); for(int j=0;j<s;++j){ scanf("%d",&v); e[v].push_back({i+n,t}); //集合内的点v与虚点i+n连边 e[i+n].push_back({v,t}); } } dijkstra(1,d1); dijkstra(n,d2); int res=INF; for(int i=1;i<=n;++i) res=min(res,max(d1[i],d2[i])); printf("Case #%d: ",cas); if(res==INF) puts("Evil John"); else{ printf("%d\n",res/2); for(int i=1;i<=n;++i)if(max(d1[i],d2[i])==res)printf("%d ",i); puts(""); } } }
浙公网安备 33010602011771号