链接:https://ac.nowcoder.com/acm/contest/949/J
来源:牛客网
小雨所在的城市一共有 m 条地铁线,分别标号为 1 号线,2 号线,……,m 号线。整个城市一共有 n个车站,编号为 1∼n 。其中坐 i 号线需要花费 ai的价格,每坐一站就需要多花费 bi 的价格。i 号线有 ci 个车站,而且这 ci 个车站都已知,如果某一站有多条地铁线经过,则可以在这一站换乘到另一条地铁线,并且能多次换乘。现在小雨想从第 s 个车站坐地铁到第 t个车站,地铁等待时间忽略不计,求最少花费的价格,若不能到达输出 -1 。(地铁是双向的,所以 s可能大于 t)
输入描述:
第一行输入四个正整数 n,m,s,t分别表示车站个数,地铁线数,起点站和终点站。
第二行到第 m+1 行,每行前三个数为 ai,bi,ci,分别表示坐 i 号线的价格,i 号线每坐一站多花的价格,i 号线车站个数。接下来 ci个数,表示 i 号线的每一个车站的编号,单调递增。
输出描述:
共一行,一个数表示最小花费,若不能到达输出 -1 。
示例1
输入
5 2 1 4 2 2 3 1 3 5 2 1 4 2 3 4 5
输出
7
说明
坐 1 号线:花费 2;
1→3:花费 2;
换乘 2 号线:花费 2;
3→4:花费 1;
所以最小总花费为 7 。
备注:
1≤n≤1000,1≤m≤500,1≤s,t≤n
1≤ai,bi≤100,1≤ci≤n,m∑i=1ci≤105
思路:每条地铁线路可以看做一层图,一层一层图之间正常建边即可。每层之间可能是相连的,怎么处理?
对应每个车站 设立一个虚点。只要每层的车站与对应的虚点连接即可。 连接方式是虚点连向车站的花费是上地铁的钱(或转地铁的钱)。
而车站连接虚点花费为0,这样达到中转的效果。
设立虚点的好处:不用根据层与层间连边,这样每层都要连,就要m*m*n条边,而设立虚点只要n*m*2。
计算也方便,直接从s的虚点可是找t的虚点的最短路就是结果。不设虚点,要对应每个车站s的地铁到每个车站t的地铁求最短路。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<vector> #include<queue> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=2100; struct node{ int u,v,w,next; }e[maxn*maxn]; int head[maxn*maxn],n,m,s,t,cnt; int a[maxn],b[maxn],d[maxn*maxn],vis[maxn*maxn]; void add(int u,int v,int w) { e[cnt]={u,v,w,head[u]}; head[u]=cnt++; } void spfa(int st)//spfa { d[st]=0; queue<int> q; q.push(st); vis[st]=1; while(!q.empty()) { int u=q.front(); q.pop(); vis[u]=0; for(int i=head[u];i!=-1;i=e[i].next) { int v=e[i].v; int w=e[i].w; if(d[v]>d[u]+w) { d[v]=d[u]+w; if(!vis[v]) { vis[v]=1; q.push(v); } } } } } int main() { int num,x,last; cnt=0; memset(head,-1,sizeof(head)); memset(d,0x3f,sizeof(d)); scanf("%d%d%d%d",&n,&m,&s,&t); for(int i=1;i<=m;i++) { scanf("%d%d%d",&a[i],&b[i],&num); for(int j=1;j<=num;j++) { scanf("%d",&x); if(j!=1) { add((i-1)*n+x,(i-1)*n+last,b[i]);//同一条线车站相连 add((i-1)*n+last,(i-1)*n+x,b[i]); } add((i-1)*n+x,n*m+x,0);//与每个虚点相连 add(n*m+x,(i-1)*n+x,a[i]); last=x;//记录之前的车站 } } spfa(n*m+s);//最短路 if(d[n*m+t]<inf) printf("%d ",d[n*m+t]); else printf("-1 "); return 0; }