小雨坐地铁
传送门
题意:n个站,m条双向地铁线路,每条地铁上地铁要掏ai元,每坐一站多掏bi元,有ci站。求从s点到t点最少花费。
思路:
显然dijkstra,但是怎么建图呢,是个好问题()。不会分层图的我大概纠结了一万年吧(如果直接朴素dijkstra的话,要记录每个点现在所处的线路,有转线和继续该线路走两种。而继续该线路走的话要记录此点在该线路的位置,有两个方向…总之我写了贼久最后放弃了)。
看了题解才知道,原来还有分层图这么个东西,了解了逻辑之后,这道题其实就是分层图最短路的裸题。思路不难,理解最重要。
逻辑大概就是,有m条地铁线路,就建m个图,再另外建一个第m+1图来存储前m个图中相互切换(转线)的花费。
为了方便理解,我就把第m+1图视作阴间,前m个图视作阳间(。
阴间的点之间并不互联,而阳间的n个点在阴间都有一个对应的点。这个对应的点与阳间的它自己互联。但是来去的花费不同。从阴间到阳间相当于转入阳间的这条地铁线,花费ai元,从阳间到阴间相当于下线,为转向同样交织在这个站点的其他线路做准备。
由于多条地铁线如果要转线,必须有相同站点,那么从该站点的阴间点到多条线路在该站点的阳间点(分布在不同的图层中)的状态转移就能模拟转线的过程。
第
i
×
n
+
x
i\times n+x
i×n+x的点就代表,第
i
i
i层(从零开始算)图的第
x
x
x个点。
更多的分层图最短路可以参考这个博客
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(x,y,z) for(int x=(int)y;x<=(int)z;x++)
const ll inf=1e18;
const int maxn=510000;
ll ans[maxn];
inline int read(){
int k=0,j=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') j=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){k=(k<<1)+(k<<3)+ch-'0';ch=getchar();}
return k*j;
}
struct node{
int v;//序号
ll cost;
bool operator <(node b)const{
return cost>b.cost;
}
};
vector<node> G[maxn];
void dijkstra(int s){
fill(ans,ans+maxn,inf);
priority_queue<node> pq;
ans[s]=0;
pq.push(node{s,0});
while(!pq.empty()){
node cur=pq.top();pq.pop();
if(ans[cur.v]<cur.cost) continue;//如果原答案更小 过
for(auto i:G[cur.v]){
if(ans[i.v]>cur.cost+i.cost){
ans[i.v]=cur.cost+i.cost;
pq.push(node{i.v,ans[i.v]});
}
}
}
}
int main(){
int n=read(),m=read(),s=read(),t=read();
forn(i,0,m-1){
int a=read(),b=read(),c=read(),x=read();
//阴间与阳间x互连
G[i*n+x].push_back(node{m*n+x,0});
G[m*n+x].push_back(node{i*n+x,a});
forn(j,2,c){
//阳间xy之间相连
int y=read();
G[i*n+x].push_back(node{i*n+y,b});
G[i*n+y].push_back(node{i*n+x,b});
//阴间与阳间y互连
G[i*n+y].push_back(node{m*n+y,0});
G[m*n+y].push_back(node{i*n+y,a});
x=y;//更新x
}
}
dijkstra(m*n+s);
if(ans[m*n+t]!=inf) printf("%lld\n",ans[m*n+t]);
else printf("-1\n");
}