[BZOJ 4152] The Captain 最短路(Dijkstra版)

题目传送门:【BZOJ 4152】

题目大意:给定平面上的 n 个点,定义 ( x1 , y1 ) 到 ( x2 , y2 ) 的距离为 min ( | x1 - x2 | , | y1 - y2 | ),求从 1 号点走到 n 号点的最小距离。(原题为费用,不过在这里是等效的)
(2 ≤ n ≤ 2*10 5 ,0 ≤ x , y ≤ 10 9

题目分析:
一道考察对题目理解的最短路的好题。

首先,根据题意,这道题肯定与最短路有关。不过我们该怎么去经过最短路的边呢?
容易看到这里题目对两点间的距离作出了定义:两点间距离为它们的 X 轴坐标值之差和 Y 轴坐标值之差的最小值。因此我们可以根据题意,令上述的最小值为两点之间的边长,然后对任意两点建边。

不过总共有 20 万个点,对任意两点建边显然是不可能的。注意到绝大部分的边都是不用建的。其实,我们只需要建立对最短路可能有贡献的边;根据题意,边长为 min ( | x1 - x2 | , | y1 - y2 | ),那么我们对每个点进行两次排序:
先对所有点的 X 坐标排序,然后对相邻的两个点连边(想一想,为什么);然后对所有点的 Y 坐标排序,再对相邻两点建边。(这里可以进行优化,但是为了理解和写起来方便,这样其实已经够了)

建好边之后,跑一遍最短路便可求出 1 号点到 n 号点的最小距离。
本题据说会卡 SPFA!

下面附上 Dijkstra 的代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int MX = 200005;
const int INF = 0x3f3f3f3f;

struct Edge{                      //之后建立的边 
    int next,to,len;
};
Edge edge[4 * MX];
struct Map{                       //输入的坐标 
    int x,y,cnt;
};
Map map[MX];
struct Heapnode{
    int d,u;
    bool operator < (const Heapnode& r) const {
        return d > r.d;
    }
};
int n,now = 0,head[MX],dis[MX];
bool vis[MX] = {0};
bool comp_x(Map i,Map j){return i.x < j.x;}
bool comp_y(Map i,Map j){return i.y < j.y;}

void adde(int u,int v,int l){
    edge[++now].to = v;
    edge[now].len = l;
    edge[now].next = head[u];
    head[u] = now;
}
void dijkstra(int s){
    priority_queue<Heapnode> q;
    memset(dis,0x3f,sizeof(dis));
    dis[s] = 0;
    q.push((Heapnode) {0,s});
    while (!q.empty()){
        int u = q.top().u;
        q.pop();
        if (vis[u]) continue;
        vis[u] = true;
        for (int i = head[u];i;i = edge[i].next){
            int v = edge[i].to;
            if (dis[u] + edge[i].len < dis[v]){
                dis[v] = dis[u] + edge[i].len;
                q.push((Heapnode) {dis[v],v});
            }
        }
    }
}
int main(){
    cin>>n;
    for (int i = 1;i <= n;i++){
        cin>>map[i].x>>map[i].y;
        map[i].cnt = i;
    }
    sort(map+1,map+n+1,comp_x);

    for (int i = 2;i <= n;i++){
        if (map[i].x - map[i-1].x <= abs(map[i].y - map[i-1].y)){
            adde(map[i-1].cnt , map[i].cnt , map[i].x-map[i-1].x);
            adde(map[i].cnt , map[i-1].cnt , map[i].x-map[i-1].x);
        }
    }
    sort(map+1,map+n+1,comp_y);

    for (int i = 2;i <= n;i++){
        if (map[i].y - map[i-1].y <= abs(map[i].x - map[i-1].x)){
            adde(map[i-1].cnt , map[i].cnt , map[i].y - map[i-1].y);
            adde(map[i].cnt , map[i-1].cnt , map[i].y - map[i-1].y);
        }
    }
    dijkstra(1);
    cout<<dis[n]<<endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值