[ZJOI2016]旅行者(网格图分治最短路)
生活随笔
收集整理的這篇文章主要介紹了
[ZJOI2016]旅行者(网格图分治最短路)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
problem
luogu-P3350
solution
據說,網格圖最短路用分治是一個人人皆知的套路。對不起我不是人
類比整體二分的算法流程。
考慮在一個 (xl,yl)?(yl,yr)(xl,yl)-(yl,yr)(xl,yl)?(yl,yr) 矩陣內處理 [ql,qr][ql,qr][ql,qr] 的詢問。
以矩陣的中界線 mid\text{mid}mid 將矩陣劃成兩半,顯然哪一維更長劃哪一維。
以中界線上的每一個點為起點跑一遍最短路,然后更新所有需要跨過這條線的詢問的答案,相當于是強制路線必須經過該點。
如果不需要跨過這條線,即詢問的起終點均在線的某一側,就分成 l,rl,rl,r 兩個部分,繼續分治下去。
時間復雜度好像都說是 O(NNlog?N)O(N\sqrt{N}\log N)O(NN?logN)。
具體見代碼即可明白。
code
#include <bits/stdc++.h> using namespace std; #define maxn 20005 #define maxq 100005 #define Pair pair < int, int > struct node { int u, v, id; }q[maxq], l[maxq], r[maxq]; vector < Pair > G[maxn]; priority_queue < Pair, vector < Pair >, greater < Pair > > que; int x[maxn], y[maxn], dis[maxn], ans[maxq]; int n, m, Q;int id( int i, int j ) { return (i - 1) * m + j; }void dijkstra( int s, int xl, int xr, int yl, int yr ) {for( int i = xl;i <= xr;i ++ )for( int j = yl;j <= yr;j ++ )dis[id(i, j)] = 0x7f7f7f7f;que.push( make_pair( dis[s] = 0, s ) );while( ! que.empty() ) {int u = que.top().second, w = que.top().first;que.pop();if( dis[u] ^ w ) continue;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first; w = G[u][i].second;if( x[v] < xl or x[v] > xr or y[v] < yl or y[v] > yr ) continue;if( dis[v] > dis[u] + w )que.push( make_pair( dis[v] = dis[u] + w, v ) );}} }void solve( int ql, int qr, int xl, int xr, int yl, int yr ) {if( ql > qr or xl > xr or yl > yr ) return;int cntl = 0, cntr = 0;if( xr - xl >= yr - yl ) {int mid = xr + xl >> 1;for( int i = yl;i <= yr;i ++ ) {dijkstra( id(mid, i), xl, xr, yl, yr );for( int j = ql;j <= qr;j ++ )ans[q[j].id] = min( ans[q[j].id], dis[q[j].u] + dis[q[j].v] );}for( int i = ql;i <= qr;i ++ ) {if( x[q[i].u] < mid and x[q[i].v] < mid ) l[++ cntl] = q[i];if( x[q[i].u] > mid and x[q[i].v] > mid ) r[++ cntr] = q[i];}for( int i = 1;i <= cntl;i ++ ) q[ql + i - 1] = l[i];for( int i = 1;i <= cntr;i ++ ) q[ql + cntl + i - 1] = r[i];solve( ql, ql + cntl - 1, xl, mid - 1, yl, yr );solve( ql + cntl, ql + cntl + cntr - 1, mid + 1, xr, yl, yr );}else {int mid = yr + yl >> 1;for( int i = xl;i <= xr;i ++ ) {dijkstra( id(i, mid), xl, xr, yl, yr );for( int j = ql;j <= qr;j ++ )ans[q[j].id] = min( ans[q[j].id], dis[q[j].u] + dis[q[j].v] );}for( int i = ql;i <= qr;i ++ ) {if( y[q[i].u] < mid and y[q[i].v] < mid ) l[++ cntl] = q[i];if( y[q[i].u] > mid and y[q[i].v] > mid ) r[++ cntr] = q[i];}for( int i = 1;i <= cntl;i ++ ) q[ql + i - 1] = l[i];for( int i = 1;i <= cntr;i ++ ) q[ql + cntl + i - 1] = r[i];solve( ql, ql + cntl - 1, xl, xr, yl, mid - 1 );solve( ql + cntl, ql + cntl + cntr - 1, xl, xr, mid + 1, yr );} }int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ )for( int j = 1, x;j < m;j ++ ) {scanf( "%d", &x );G[id(i, j)].push_back( make_pair( id(i, j + 1), x ) );G[id(i, j + 1)].push_back( make_pair( id(i, j), x ) );}for( int i = 1;i < n;i ++ )for( int j = 1, x;j <= m;j ++ ) {scanf( "%d", &x );G[id(i, j)].push_back( make_pair( id(i + 1, j), x ) );G[id(i + 1, j)].push_back( make_pair( id(i, j), x ) );}for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )x[id(i, j)] = i, y[id(i, j)] = j;scanf( "%d", &Q );for( int i = 1, a1, b1, a2, b2;i <= Q;i ++ ) {scanf( "%d %d %d %d", &a1, &b1, &a2, &b2 );q[i] = (node){ id(a1, b1), id(a2, b2) }, q[i].id = i;}memset( ans, 0x7f, sizeof( ans ) );solve( 1, Q, 1, n, 1, m );for( int i = 1;i <= Q;i ++ ) printf( "%d\n", ans[i] );return 0; }總結
以上是生活随笔為你收集整理的[ZJOI2016]旅行者(网格图分治最短路)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [ZJOI2015]幻想乡 Wi-Fi
- 下一篇: 暴走漫画制作器怎么用?暴走漫画制作器详细