1. 程式人生 > >CSU 1808 - 地鐵 - [最短路變形]

CSU 1808 - 地鐵 - [最短路變形]

names time ref bob href esp 地鐵站 ans main

題目鏈接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1808

Time limit: 5000 ms Memory limit: 131072 kB

Bobo 居住在大城市 ICPCCamp。

ICPCCamp 有 n 個地鐵站,用 1,2,…,n 編號。 m 段雙向的地鐵線路連接 n 個地鐵站,其中第 i 段地鐵屬於 c i 號線,位於站 a i,b i 之間,往返均需要花費 t i 分鐘(即從 a i 到 b i需要 t i 分鐘,從 b i 到 a i 也需要 t i 分鐘)。 眾所周知,換乘線路很麻煩。如果乘坐第 i 段地鐵來到地鐵站 s,又乘坐第 j 段地鐵離開地鐵站 s,那麽需要額外花費 |c i
-c j | 分鐘。註意,換乘只能在地鐵站內進行。 Bobo 想知道從地鐵站 1 到地鐵站 n 所需要花費的最小時間。

Input

輸入包含不超過 20 組數據。 每組數據的第一行包含兩個整數 n,m (2≤n≤10 5,1≤m≤10 5). 接下來 m 行的第 i 行包含四個整數 a i,b i,c i,t i (1≤a i,b i,c i≤n,1≤t i≤10 9). 保證存在從地鐵站 1 到 n 的地鐵線路(不一定直達)。

Output

對於每組數據,輸出一個整數表示要求的值。

Sample Input

3 3
1 2 1 1
2 3 2 1
1 3 1 1
3 3
1 2 1 1
2 3 2 1
1 3 1 10
3 2
1 2 1 1
2 3 1 1

Sample Output

1
3
2

題解:

如果只記錄到某個節點x的最短路長度d[x],並且記錄對應於d[x],是坐哪號線來到節點x的,這樣顯然是錯誤的。

原因比如這樣的樣例:

3 3
1 2 1 2
1 2 3 3
2 3 3 5

可以看出,d[x]要擴展到d[x][c],即這題的狀態有兩個量決定:到了節點x,最後乘坐的是c號線;

那麽,如果我們把節點x用若幹條邊Edge(u1→x)…Edge(uk→x)來代替,那麽我們就相當於把d[x]要擴展到d[x][c]了;

所以我們可以直接把邊當成點,對邊做最短路(用堆優化dijkstra)。

AC代碼:

#include<cstdio>
#include
<cmath> #include<cstring> #include<iostream> #include<vector> #include<algorithm> #include<queue> using namespace std; typedef long long LL; typedef pair<LL,int> Pair; const LL INF=1e18; const int maxn=1e5+10; const int maxm=2e5+10; //無向邊拆成兩條有向邊 int n,m; struct Edge{ int u,v,c; int next; LL t; }; Edge E[maxm]; int head[maxn],ne; void init() { ne=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int c,LL t) { E[ne].u=u, E[ne].v=v, E[ne].c=c, E[ne].t=t; E[ne].next=head[u]; head[u]=ne++; } LL ans; LL d[maxm]; bool vis[maxm]; void dijkstra(int st) { priority_queue< Pair, vector<Pair>, greater<Pair> > Q; memset(vis,0,sizeof(vis)); for(int i=0;i<ne;i++) d[i]=INF; ans=INF; for(int i=head[st];i!=-1;i=E[i].next) { d[i]=E[i].t; Q.push(Pair(d[i],i)); } while(!Q.empty()) { int x=Q.top().second; Q.pop(); if(vis[x]) continue; vis[x]=1; if(E[x].v==n) ans=min(ans,d[x]); for(int y=head[E[x].v];y!=-1;y=E[y].next) { if(vis[y]) continue; if(d[y]>d[x]+E[y].t+abs(E[y].c-E[x].c)) { d[y]=d[x]+E[y].t+abs(E[y].c-E[x].c); Q.push(Pair(d[y],y)); } } } } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); for(int i=1;i<=m;i++) { int u,v,c; LL t; scanf("%d%d%d%lld",&u,&v,&c,&t); addedge(u,v,c,t); addedge(v,u,c,t); } dijkstra(1); printf("%lld\n",ans); } }
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long LL;
typedef pair<LL,int> Pair;

const LL INF=1e18;
const int maxn=1e5+10;
const int maxm=2e5+10; //無向邊拆成兩條有向邊

int n,m;

struct Edge{
    int u,v,c;
    LL t;
};
vector<Edge> E;
vector<int> G[maxn];
void init(int l,int r)
{
    E.clear();
    for(int i=l;i<=r;i++) G[i].clear();
}
void addedge(int u,int v,int c,LL t)
{
    E.push_back((Edge){u,v,c,t});
    G[u].push_back(E.size()-1);
}

LL ans;
LL d[maxm];
bool vis[maxm];
void dijkstra(int st)
{
    priority_queue< Pair, vector<Pair>, greater<Pair> > Q;

    memset(vis,0,sizeof(vis));
    for(int i=0;i<E.size();i++) d[i]=INF;
    ans=INF;

    for(int i=0;i<G[st].size();i++)
    {
        int x=G[st][i];
        d[x]=E[x].t;
        Q.push(Pair(d[x],x));
    }
    while(!Q.empty())
    {
        int x=Q.top().second; Q.pop();

        if(vis[x]) continue;
        vis[x]=1;
        if(E[x].v==n) ans=min(ans,d[x]);

        for(int i=0;i<G[E[x].v].size();i++)
        {
            int y=G[E[x].v][i];
            if(vis[y]) continue;
            if(d[y]>d[x]+E[y].t+abs(E[y].c-E[x].c))
            {
                d[y]=d[x]+E[y].t+abs(E[y].c-E[x].c);
                Q.push(Pair(d[y],y));
            }
        }
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init(1,n);
        for(int i=1;i<=m;i++)
        {
            int u,v,c; LL t;
            scanf("%d%d%d%lld",&u,&v,&c,&t);
            addedge(u,v,c,t);
            addedge(v,u,c,t);
        }

        dijkstra(1);
        printf("%lld\n",ans);
    }
}

註:兩份代碼的區別是分別用鏈式前向星和vector鄰接表存圖。

CSU 1808 - 地鐵 - [最短路變形]