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

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

题目大意

原题传送门

大意

给定 N(2N105) N ( 2 ≤ N ≤ 10 5 ) 个城市及 M(0M2×105) M ( 0 ≤ M ≤ 2 × 10 5 ) 条地铁线路,第 i i 条线路连接pi qi q i 两座城市,由编号为 ci c i 的公司运营,若在同一公司之间的地铁线路之间换乘,花费为0,若在不同公司之间的线路换乘,则花费为1,求从城市 1 1 到城市N的最小花费。若无解则输出 1 − 1 .

思路

当我拿到这一道题时,第一反应就是扔给SPFA,建图时边上只保存公司编号,在计算权值时按公司差异计算,具体实现如下:
这里写图片描述
然后。。。
这里写图片描述
就没有然后了。。。

一天之后。。。

当我看了某dalao的题解之后。。。

猛然明白了:拆边!

我们以样例为例:

建出的普通图:
这里写图片描述
将边拆掉后重建的图:
这里写图片描述
具体方法:我们将边 (pi,qi,ci) ( p i , q i , c i ) 拆成两点 (pi,ci) ( p i , c i ) (qi,ci) ( q i , c i ) ,在 pi p i (pi,ci) ( p i , c i ) qi q i (qi,ci) ( q i , c i ) (pi,ci) ( p i , c i ) (qi,ci) ( q i , c i ) 之间连边,并将权值分别赋为 1 1 ,1, 0 0 ,意思就是在pi上车需要1元,在 pi p i , qi q i 之间坐车消耗0元,在 qi q i 下车又需要1元。

意思就是如果你在 u u 站下车,坐的是公司ua的车,所以当前你在公司 ua u a 的站台上,若你想前往公司 ub u b 的站台乘坐他的车,你就必须去站 u u ,并付1元;然后你才能前往站台ub。即你在 u u 站换乘的顺序是:站台ua→站 u 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值