D88 最短路+虚点建图 Dijkstra 算法 HDU 5521 Meeting

D88 最短路+虚点建图 Dijkstra 算法 HDU 5521 Meeting_哔哩哔哩_bilibili

 

Problem - HDU 5521 Meeting

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)$

  image

样例模拟:

方案一:男从 1 号出发,经 1 秒到 3 号;女从 5 号出发,经 3 秒到 3 号,所以用时 3 秒,会面地点 3 号

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

  image

集合数 $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("");
    }
  }
}
 

 

posted @ 2026-02-26 00:16  董晓  阅读(96)  评论(0)    收藏  举报