1. 程式人生 > >2015年ACM/ICPC瀋陽賽區 M題(思維+堆優化的Dijkstra演算法)

2015年ACM/ICPC瀋陽賽區 M題(思維+堆優化的Dijkstra演算法)

題意:

給你n(n<=1e5)個點,m個完全圖集合(每個集合包含權值v,集合中的點數cnt和集合裡的點的標號,表示集合裡的點構成無向完全圖,兩點之間權值為v(時間))。

保證集合中所有的點的數量不超過1e6。

兩個人分別從1點和n點走,只可以在一個點相遇(一個人可以等另一個人),求最短相遇時間和最短時間的前提下在哪些編號的點相遇時間都是最短。

如果無法相遇,輸出“Evil John”。

思路:

比賽的時候沒做出來,不過看了題解之後,感覺構圖真是巧妙。可能很簡單吧,比賽的時候過了好多人,但是我卻沒想到。。。

考慮題目中特殊條件“保證集合中所有的點的數量不超過1e6。”,顯然不能每個集合每個點相互建邊。因此要將集合建邊。

但是我們要求的是1點和n點的單源最短路 怎麼能用集合建圖呢???

假設我們現在從1點開始走,如果想走到另一個點,只能走集合。於是,我們把集合編號 n+1~n+m+1。

對於集合中每個點 向集合建邊 add(x,n+i,v);add(n+i,x,0)。這樣只要一個非這個集合中的點想經過集合中的點,就必定要經過這條邊,即花費時間。進入這個集合後,走向中集合中的任何點花費就都為0了(因為進來的點已經加了花費,走任何點都相當於直接走過去,花費就是v)。這樣經過這個集合時時間就僅增加了v。

然後套個快一點的堆優化的Dijkstra演算法板子分別求兩次單源最短路,然後列舉每個相遇的點,取個最小值,對於相遇時間等於最小值的點就可以直接輸出了。

程式碼:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3fLL
#define pa pair<int,int>
using namespace std;
const int maxn = 2200010;
struct node
{
    int to,next;
    ll v;
};
struct Dijkstra
{
  node edge[maxn];
  int cnt,head[maxn],n;
  ll dis[maxn];
  void init(int nn)
  {
      n=nn;
      cnt=0;
      for(int i=0;i<=n;i++) head[i]=0;
  }
  void add(int u,int v,int w)
  {
    cnt++;
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
    edge[cnt].v=w;
  }

  void dijkstra(int s)
  {
    priority_queue<pa,vector<pa>,greater<pa> >q;
    int i,now;
    for (i=1;i<=n;i++)
    dis[i]=inf;
    dis[s]=0;
    q.push(make_pair(0,s));
    while (!q.empty())
      {
        now=q.top().second;
        q.pop();
        for (i=head[now];i;i=edge[i].next)
          if (dis[now]+edge[i].v<dis[edge[i].to])
            {
              dis[edge[i].to]=dis[now]+edge[i].v;
              q.push(make_pair(dis[edge[i].to],edge[i].to));
            }
      }
  }
}D1,DN;
int n,m;
ll v;
int main()
{
    int T,cas=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        D1.init(n+m+1);
        DN.init(n+m+1);
        for(int i=0;i<m;i++)
        {
            int cnt;
            scanf("%lld%d",&v,&cnt);
            for(int j=0;j<cnt;j++)
            {
                int x;
                scanf("%d",&x);
                D1.add(x,n+i+1,v);
                D1.add(n+i+1,x,0);
                DN.add(x,n+i+1,v);
                DN.add(n+i+1,x,0);
            }
        }
        D1.dijkstra(1);
        DN.dijkstra(n);
        ll mi=inf;
        for(int i=1;i<=n;i++)
        {
            D1.dis[i]=max(D1.dis[i],DN.dis[i]);
            mi=min(mi,D1.dis[i]);
        }
        printf("Case #%d: ",cas++);
        if(mi==inf) puts("Evil John");
        else
        {
            printf("%d\n",mi);
            bool fg=0;
            for(int i=1;i<=n;i++)
            if(mi==D1.dis[i]){
                if(!fg) printf("%d",i),fg=1;
                else printf(" %d",i);
            }
            puts("");
        }
    }
    return 0;
}