洛谷P3831[SHOI2012]回家的路(spfa) 题解

94 篇文章 0 订阅

题目来源:

https://www.luogu.org/problemnew/show/P3831

题目描述:

 

题目背景

SHOI2012 D2T1

题目描述

2046 年 OI 城的城市轨道交通建设终于全部竣工,由于前期规划周密,建成后的轨道交通网络由 2n2n 条地铁线路构成,组成了一个 nn 纵 nn 横的交通网。如下图所示,这 2n2n 条线路每条线路都包含 nn 个车站,而每个车站都在一组纵横线路的交汇处。

出于建设成本的考虑,并非每个车站都能够进行站内换乘,能够进行站内换乘的地铁站共有 mm 个,在下图中,标上方块标记的车站为换乘车站。已知地铁运行 1 站需要 2 分钟,而站内换乘需要步行 1 分钟。Serenade 想要知道,在不中途出站的前提下,他从学校回家最快需要多少时间(等车时间忽略不计)。

输入输出格式

输入格式:

 

第一行有两个整数 n,mn,m 。

接下去 mm 行每行两个整数 x,yx,y ,表示第 xx 条横向线路与第 yy 条纵向线路的交

汇站是站内换乘站。

接下去一行是四个整数 x_1,y_1,x_2,y_2x1​,y1​,x2​,y2​ 。表示 Serenade 从学校回家时,在第 x_1x1​ 条横向线路与第 y_1y1​ 条纵向线路的交汇站上车,在第 x_2x2​ 条横向线路与第 y_2y2​ 条纵向线路的交汇站下车。

 

输出格式:

 

输出文件只有一行,即 Serenade 在合理选择线路的情况下,回家所需要的时间。如果 Serenade 无法在不出站换乘的情况下回家,请输出-1。

 

输入输出样例

输入样例#1: 复制

2 1
1 2
1 1 2 2

输出样例#1: 复制

5

输入样例#2: 复制

6 9
2 1
2 5
3 2
4 4
5 2
5 6
6 1
6 3
6 4
1 1 4 6

输出样例#2: 复制

27

输入样例#3: 复制

6 10
2 1
2 5
3 2
4 4
5 2
5 6
6 1
6 3
6 4
6 6
1 1 4 6

输出样例#3: 复制

26

说明

对于 30%的数据, n\le 50,m\le 1000n≤50,m≤1000 ;

对于 60%的数据, n\le 500,m\le 2000n≤500,m≤2000 ;

对于 100%的数据, n\le 20000,m\le 100000n≤20000,m≤100000 ;

解题思路:

       这题是一题分层图求最短路的问题,还是比较经典的,我们用spfa来求,首先我们知道要走到终点一定是通过起点和终点和m个中转站,所以在建图的时候只要对这些点建边就行,地铁如果不转站只能只能左右或者上下走,所以我们可以用第一层图存左右走的点,第二层图存上下走的点,而第一层和第二层对应的点应该要建一条边权为1的点,作为转站的代价,这样我们就可以跑一遍spfa求出起点和终点的最短路。。。

代码:

#include <cmath>
#include <queue>
#include <cstdio>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 210000000
using namespace std;
vector<pair<int,int> >E[300005];
void add(int from,int to,int val)
{
    E[from].push_back(make_pair(to,val));
    E[to].push_back(make_pair(from,val));
}
struct newt {
	int x,y,id;
}dian[300005];
bool cmp1(newt a,newt b)
{
    if(a.x==b.x)return a.y<b.y;
    return a.x<b.x;
}
bool cmp2(newt a,newt b)
{
    if(a.y==b.y)return a.x<b.x;
    return a.y<b.y;
}
int n,m;
queue<int> q;
int dis[300005],S,T;
bool vis[300005];
void spfa()
{
    for(int i=1;i<=2*m+4;i++)dis[i]=inf;
    dis[S]=0,vis[S]=1;q.push(S);
    while(!q.empty())
    {
        int now=q.front();q.pop();vis[now]=0;
        for(int i=0;i<E[now].size();i++)
        {
            int to=E[now][i].first,val=E[now][i].second;
            if(dis[to]>dis[now]+val)
            {
                dis[to]=dis[now]+val;
                if(!vis[to])
                {
                    vis[to]=1;
                    q.push(to);
                }
            }
        }
    }
}
int main()
{
    cin>>n>>m;S=m+1,T=m+2;
    for(int i=1;i<=m+2;i++)
    {
        cin>>dian[i].x>>dian[i].y;
        dian[i].id=i;
    }
    sort(dian+1,dian+m+3,cmp1);
    for(int i=1;i<m+2;i++)
    if(dian[i].x==dian[i+1].x)add(dian[i].id,dian[i+1].id,2*(dian[i+1].y-dian[i].y));
    sort(dian+1,dian+m+3,cmp2);
    for(int i=1;i<m+2;i++)
    if(dian[i].y==dian[i+1].y)add(dian[i].id+m+2,dian[i+1].id+m+2,2*(dian[i+1].x-dian[i].x));
    for(int i=1;i<=m;i++)
    add(i,i+m+2,1);
    add(m+1,m*2+3,0);add(m+2,m*2+4,0);
    spfa();
    if(dis[T]==inf)
    {
        puts("-1");
        return 0;
    }
    printf("%d\n",dis[T]);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值