原题:2017计蒜之道复赛Problem D
标签:建图,SPFA
题目链接
百度地图上有 n 个城市,城市编号依次为 1 到 n。地图中有若干个城市群,编号依次为 1 到 m。每个城市群包含一个或多个城市;每个城市可能属于多个城市群,也可能不属于任何城市群。
地图中有两类道路。第一类道路是 城市之间的快速路,两个城市 u,v 之间增加一条距离为 c 的边;第二类道路是 城市群之间的高速路,连接两个城市群 a,b,通过这条高速路,城市群 a 里的每个城市与城市群b 里的每个城市之间两两增加一条距离为 c 的边。图中所有边均为无向边。
你需要计算从城市 s 到城市 t 的最短路。
第一行输入n, m,分别表示城市总数和城市群总数。
接下来一共输入 m 行。
第 i 行首先输入一个k[i] (1≤k[i]≤n),表示第 i个城市群中的城市数为k[i]。接下来输入 k[i]个数,表示第 i 个城市群中每个城市的编号(保证一个城市群内的城市编号不重复且合法, ∑k[i]≤20000)
下一行输入一个整数m1 (0≤m[1]≤n),表示有 m1条第一类道路,即城市之间的快速路。
接下来 m1行,每行输入三个整数u[i] ,v[i](1≤u[i] ,v[i]≤n),c[i] (1≤c[i]≤10^6 ),分别表示快速路连接的两个城市编号和边的距离。
下一行输入一个整数 m2(0≤m2 ≤m),表示有 m2条第二类道路,即城市群之间的高速路。
接下来 m2行,每行输入三个整数 a[i] ,b[i] (1≤a[i] ,b[i]≤m),l[i] (1≤l[i]≤10^6 ),分别表示快速路连接的两个城市群编号和边的距离。
最后一行输入 s,t(1≤s,t≤n),表示起点和终点城市编号。
输入范围
对于30%的数据,n<=200,m=0
对于50%的数据,n,m<=200
对于100%的数据,2<=n<=20000,0<=m<=20000
输出一个整数,表示城市 s 到城市 t 到最短路。如果不存在路径,则输出-1。
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
这道题大意是给出城市及城市圈,已经它们之间的距离,求最短路,我们不难想到用spfa。所以这里重点讲如何建图。
建图就是对于城市之间的边保持不变,对于城市圈,有性质“城市群 a 里的每个城市与城市群b 里的每个城市之间两两增加一条距离为 c 的边”,基于这个性质,我们可以将城市圈新建为一个点,为了避免与已有点的编号重合,我们令城市圈i的点编号为i+n。
有了这个点,我们可以将城市圈中的城市的点与城市圈所表示的点连起来,当然,路径长度为0。
注意到一个问题:这样做会导致同一城市圈中的点(假设为A,B)的距离变为0。(想一想,为什么)为了避免这种情况的出现,我们可以将一个城市圈用两个点表示,将城市圈中的城市的点与城市圈所表示的点的路径单向化。这样就可以避免上述问题。令城市圈i所表示的两个点编号分别为i+n,i+2*n(这意味着,邻接表需要开到3倍大小)。
当连接i,j两个城市圈时,我们只需连接(i+n,j+2*n),(j+n,i+2*n)两条单向边即可。
详见代码。
#include
#include
#include
#define maxn 100050
#define add(u,v,vv) (to[++top]=head[u],head[u]=top,w[top]=v,val[top]=vv)
#define For(x) for(int h=head[x],o=w[h],v=val[h];h;o=w[h=to[h]],v=val[h])
using namespace std;
int q[maxn],head[maxn],val[maxn],to[maxn],w[maxn],i,j,l,r,d[maxn],s,t,n,m,k,top,m1,m2,x,y,z;
bool f[maxn];
void spfa()
{
memset(f,0,sizeof(f)); memset(d,0x7f,sizeof(d));
d[s]=0; f[s]=1; q[l=r=0]=s;
while (l<=r)
{
int x=q[l];
For (x)
{
if (v+d[x]x];
if (!f[o])
{
f[o]=1; q[++r]=o;
}
}
}
f[x]=0;
l++;
}
}
int main()
{
scanf("%d%d",&n,&m);
for (i=1;i<=m;i++)
{
scanf("%d",&k);
for (j=1;j<=k;j++) scanf("%d",&s),add(s,i+n,0),add(i+n*2,s,0);
}
scanf("%d",&m1);
for (i=1;i<=m1;i++) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
scanf("%d",&m2);
for (i=1;i<=m2;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x+n,y+2*n,z); add(y+n,x+n*2,z);
}
scanf("%d%d",&s,&t);
spfa();
if (d[t]!=d[0]) printf("%d",d[t]);else printf("-1");
return 0;
}