Returning Home(建圖、最短路)

miracle_pbc發表於2020-10-10

題意

給定一個 n ∗ n n*n nn的網格,在這個網格中給出兩個座標,分別作為起點和終點。並且在圖中存在 m m m個傳送陣,傳送陣的座標已知。從起點開始向終點走,相鄰兩個位置之間花費一個單位,如果走到與某個傳送陣同行或者同列,那麼可以不耗花費的傳送到那個傳送陣的位置。問從起點到終點,最少花費多少單位。

資料範圍

1 ≤ n ≤ 1 0 9 1 \leq n \leq 10^9 1n109
0 ≤ m ≤ 1 0 5 0 \leq m \leq 10^5 0m105

思路

如果傳送陣直接建一個完全圖,那麼顯然會爆空間和時間,因此需要考慮更簡潔的建圖方式。
首先我們可以考慮到,兩個傳送陣之間的距離為 m i n ( x 2 − x 1 , y 2 − y 1 ) min(x_2 - x_1, y_2 - y_1) min(x2x1,y2y1),這就等價於兩個傳送陣在同一行或者同一列上。因此,我們可以對傳送陣的橫座標排序,相鄰兩個連起來;再對傳送陣的縱座標排序,相鄰兩個連起來。因為要求的是最短路,所以兩點之間出現重邊也沒關係。
最後,將起點作為 0 0 0號點,終點作為 m + 1 m+1 m+1號點,起點向每一個傳送陣連一條邊,終點向每個傳送陣連一條邊,起點和終點之間再連一條邊。值得注意的是,起點到傳送陣的距離計算方式和傳送陣到終點的距離計算方式是不同的,因為一個可以傳送,另一個不能傳送。
這樣圖就建好了,然後跑一個Dijkstra演算法就完成了。

程式碼

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>

using namespace std;

typedef long long ll;
typedef pair<ll,int> pli;
typedef pair<int,ll> pil;

const int N = 100010, M = 10 * N;
const ll inf = 1e18;

int sz,n,S,T;
int h[N], e[M], ne[M], idx;
ll w[M];
ll xx[N], yy[N];
ll dist[N];
bool st[N];
pli x_id[N], y_id[N];

void add(int a,int b,ll c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

void dijkstra()
{
    for(int i=0;i<=n+1;i++) dist[i] = inf;
    memset(st,0,sizeof(st));
    dist[0] = 0;
    priority_queue<pli,vector<pli>,greater<pli> > heap;
    heap.push({dist[0],0});
    while(heap.size()){
        auto t = heap.top();
        heap.pop();
        int ver = t.second;
        ll distance = t.first;
        if(st[ver]) continue;
        st[ver] = true;
        for(int i=h[ver];~i;i=ne[i]){
            int j = e[i];
            if(dist[j]>distance+w[i]){
                dist[j] = distance + w[i];
                heap.push({dist[j],j});
            }
        }
    }
}

int main()
{
    cin >> sz >> n;
    memset(h,-1,sizeof(h));
    ll sx,sy,fx,fy;
    cin >> sx >> sy >> fx >> fy;
    ll dd = abs(sx - fx) + abs(sy - fy);
    add(0,n+1,dd), add(n+1,0,dd);
    for(int i=1;i<=n;i++){
        cin >> xx[i] >> yy[i];
        x_id[i] = {xx[i],i}, y_id[i] = {yy[i],i};
    }
    for(int i=1;i<=n;i++){
        ll d = min(abs(sx-xx[i]),abs(sy-yy[i]));
        add(0,i,d), add(i,0,d);
    }
    for(int i=1;i<=n;i++){
        ll d = abs(fx-xx[i]) + abs(fy-yy[i]);
        add(n+1,i,d), add(i,n+1,d);
    }
    sort(x_id+1,x_id+n+1);
    sort(y_id+1,y_id+n+1);
    for(int i=1;i<n;i++){
        int id1 = x_id[i].second, id2 = x_id[i+1].second;
        ll d = abs(x_id[i].first - x_id[i+1].first);
        add(id1,id2,d), add(id2,id1,d);
    }
    for(int i=1;i<n;i++){
        int id1 = y_id[i].second, id2 = y_id[i+1].second;
        ll d = abs(y_id[i].first - y_id[i+1].first);
        add(id1,id2,d), add(id2,id1,d);
    }
    dijkstra();
    cout << dist[n+1] << endl;
    return 0;
}

相關文章