1. 程式人生 > >【AtCoder】【圖論】ARC061Eすぬけ君の地下鉄旅行 / Snuke's Subway Trip

【AtCoder】【圖論】ARC061Eすぬけ君の地下鉄旅行 / Snuke's Subway Trip

すぬけ君の地下鉄旅行 / Snuke’s Subway Trip

題目大意

原題傳送門

大意

給定N(2N105)個城市及M(0M2×105)條地鐵線路,第i條線路連線piqi兩座城市,由編號為ci的公司運營,若在同一公司之間的地鐵線路之間換乘,花費為0,若在不同公司之間的線路換乘,則花費為1,求從城市1到城市N的最小花費。若無解則輸出1.

思路

當我拿到這一道題時,第一反應就是扔給SPFA,建圖時邊上只儲存公司編號,在計算權值時按公司差異計算,具體實現如下:
這裡寫圖片描述
然後。。。
這裡寫圖片描述
就沒有然後了。。。

一天之後。。。

當我看了某dalao的題解之後。。。

猛然明白了:拆邊!

我們以樣例為例:

建出的普通圖:
這裡寫圖片描述
將邊拆掉後重建的圖:
這裡寫圖片描述
具體方法:我們將邊(pi,qi,ci)拆成兩點(pi,ci)(qi,ci),在pi(pi,ci)qi(qi,ci)(pi,ci)(qi,ci)之間連邊,並將權值分別賦為1,1,0,意思就是在pi上車需要1元,在pi,qi之間坐車消耗0元,在qi下車又需要1元。

意思就是如果你在u站下車,坐的是公司

ua的車,所以當前你在公司ua的站臺上,若你想前往公司ub的站臺乘坐他的車,你就必須去站u,並付1元;然後你才能前往站臺ub。即你在u站換乘的順序是:站臺ua→站u→站臺ub

接下來就是SPFA的事情了(你用Dijskra也不錯)。

實現細節

我們可以使用C++STL標準庫容器map來實現對拆開的邊取得編號(Hash也是個不錯的選擇)。

注意在開陣列的時候要擴大幾倍(你可以用自己的AC率來嘗試一下)。

但是,若用上面的方法就會出問題:我們程式得出的答案不對!

原因就是:我們在換乘的時候,是花了2元的,但題目中只需1元。

所以:注意在輸出答案時除以二(你也可以用自己的AC率來嘗試一下)。

正解程式碼

這才是你們最喜歡的東西。

#include<cstdio>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
const int Maxn=1e5;
const int INF=0x3f3f3f3f;
struct Edge {
    int to,cost;
    Edge(int a,int b){to=a,cost=b;}
};
vector<Edge> G[8*Maxn+5];
void AddEdge(int u,int v,int w) {
    G[u].push_back(Edge(v,w));
    G[v].push_back(Edge(u,w));
}

int dist[8*Maxn+5];
bool vis[8*Maxn+5];
void SPFA(int s) {
    memset(dist,0x3f,sizeof dist);
    memset(vis,false,sizeof vis);
    queue<int> q;

    q.push(s);
    vis[s]=true,dist[s]=0;

    while(!q.empty()) {
        int u=q.front();
        q.pop();vis[u]=false;

        for(int i=0;i<G[u].size();i++) {
            int v=G[u][i].to,w=G[u][i].cost;
            if(dist[v]>dist[u]+w) {
                dist[v]=dist[u]+w;
                if(!vis[v]) {
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
}

map<pair<int,int>,int> P;
int cnt;
int Getid(int u,int c) {
    if(P.find(make_pair(u,c))!=P.end())
        return P[make_pair(u,c)];
    return P[make_pair(u,c)]=++cnt;
}

int N,M;
int main() {
    #ifdef LOACL
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    scanf("%d %d",&N,&M);
    cnt=N;
    for(int i=1;i<=M;i++) {
        int u,v,c;
        scanf("%d %d %d",&u,&v,&c);
        int t1=Getid(u,c);
        int t2=Getid(v,c);
        AddEdge(t1,t2,0);
        AddEdge(u,t1,1);
        AddEdge(v,t2,1);
    }
    SPFA(1);
    if(dist[N]==INF)puts("-1");
    else printf("%d\n",dist[N]/2);
    return 0;
}