2017 计蒜客复赛 D.百度地图导航

来源:互联网 发布:商业数据分析报告 编辑:程序博客网 时间:2024/05/21 22:33
AC率:17.24% 时限:1000ms 内存限制:131072K

百度地图上有 n 个城市,城市编号依次为 1 到 n。地图中有若干个城市群,编号依次为 1 到 m。每个城市群包含一个或多个城市;每个城市可能属于多个城市群,也可能不属于任何城市群。


地图中有两类道路。第一类道路是 城市之间的快速路,两个城市 u,v 之间增加一条距离为 c 的边;第二类道路是 城市群之间的高速路,连接两个城市群 a,b,通过这条高速路,城市群 a 里的每个城市与城市群 b 里的每个城市之间两两增加一条距离为 c 的边。图中所有边均为无向边。


你需要计算从城市 s 到城市 t 的最短路。

输入格式

第一行输入 n(1≤n≤20000),m(0≤m≤20000),分别表示城市总数和城市群总数。

接下来一共输入 m 行。

第 i 行首先输入一个 ki(1≤k​i≤n),表示第 i 个城市群中的城市数为 ki。接下来输入ki个数,表示第 i 个城市群中每个城市的编号(保证一个城市群内的城市编号不重复且合法,且ki之和不超过20000)。

下一行输入一个整数 m1(0≤m1≤20000),表示有 m1条第一类道路,即城市之间的快速路。

接下来 m1行,每行输入三个整数 ui,vi,ci(1≤ui,v​i≤n),c​i(1≤c​i≤10^​6),分别表示快速路连接的两个城市编号和边的距离。

下一行输入一个整数 m2(0≤m2≤20000),表示有 m2条第二类道路,即城市群之间的高速路。

接下来 m2行,每行输入三个整数 ai​,b​i(1≤ai,bi≤m),li(1≤li≤10^6),分别表示快速路连接的两个城市群编号和边的距离。

最后一行输入 s,t(1≤s,t≤n),表示起点和终点城市编号。

输出格式

输出一个整数,表示城市 s 到城市 t 到最短路。如果不存在路径,则输出-1。

样例说明

1 -> 2 - > 5或者1 -> 4 -> 5是最短的路径,总长度为 12。

样例输入

5 4
2 5 1
2 2 4
1 3
2 3 4
2
1 2 9
1 5 18
2
1 2 6
1 3 10
1 5
样例输出


12

提示

如果采用暴力建边,边的数量太多内存受不了,因此我们需要增加点的数量减少边的数量。

第一类路很简单我们就直接跳过。这题需要把每个城市群拆成两个点,表示该城市群的入口和出口,因为对于第二类路城市群之间是互通的,不然无法避免出现在跑最短路过程中直接通过汇点跑向属于同一个城市群的错误走法。之后每个城市群中的城市建立从城市到出口,从入口到城市的有向边,权值为0。对于城市群我们建立从城市群ai的出口到城市群bi的入口,ai的入口到bi的出口,权值就为题目所给的li。之后直接上最短路即可,存图建议用前向星比较合适。

我的代码中规定点编号为0~m-1是m个城市群的入口,m~2*m-1是m个城市群的出口,2*m~2*m+n-1是n个城市。

注意跑最短路过程中可能会爆int。

示例程序

#include <cstdio>#include <cstring>#include <queue>using namespace std;struct jj{    int v,next,cost;}w[1000000];int h[60050],numw;void insert(int u,int v,int cost){    w[numw].v=v;    w[numw].cost=cost;    w[numw].next=h[u];    h[u]=numw++;}queue<int>q;long long spfa(int s,int t){    int v[60050],i;    long long dis[60050];    for(i=0;60050>i;i++)    {        v[i]=0;        dis[i]=10000000000000000;    }    dis[s]=0;    q.push(s);    while(q.empty()==0)    {        s=q.front();        q.pop();        v[s]=0;        for(i=h[s];i!=-1;i=w[i].next)        {            if(dis[w[i].v]>dis[s]+w[i].cost)            {                dis[w[i].v]=dis[s]+w[i].cost;                if(v[w[i].v]==0)                {                    v[w[i].v]=1;                    q.push(w[i].v);                }            }        }    }    if(dis[t]==10000000000000000)    {        dis[t]=-1;    }    return dis[t];}int main(){    int i,i1,n,m,m1,k,u,v,cost;    scanf("%d %d",&n,&m);    memset(h,-1,sizeof(h));    numw=0;    for(i=0;m>i;i++)    {        scanf("%d",&k);        for(i1=1;k>=i1;i1++)        {            scanf("%d",&u);            insert(i,u-1+2*m,0);//城市群入口到城市建边,权值为0            insert(u-1+2*m,i+m,0);//城市到城市群出口建边,权值为0        }    }    scanf("%d",&m1);    for(i=1;m1>=i;i++)//对于第一类路直接建边记可    {        scanf("%d %d %d",&u,&v,&cost);        insert(u-1+2*m,v-1+2*m,cost);        insert(v-1+2*m,u-1+2*m,cost);    }    scanf("%d",&m1);    for(i=1;m1>=i;i++)//第二类路    {        scanf("%d %d %d",&u,&v,&cost);        insert(v-1+m,u-1,cost);//城市群v的出口与城市群u的入口建边        insert(u-1+m,v-1,cost);//城市群u的出口与城市群v的入口建边    }    scanf("%d %d",&u,&v);    printf("%lld\n",spfa(u-1+2*m,v-1+2*m));    return 0;}