題目連接:http://codeforces.com/problemset/problem/677/Dios
題意:c++
有 $n \times m$ 的網格,每一個網格上有一個棋子,棋子種類爲 $t[i][j]$,棋子的種類數爲 $p$。優化
如今出發點爲 $(1,1)$,必須按照種類 $1 \sim p$ 進行移動,即從種類 $x$ 的棋子出發,下一個目標必須是 $x+1$ 才行,直到走到種類爲 $p$ 的棋子就終止。求最短路徑。spa
題解:code
咱們先把棋子按照種類分組,分紅 $p$ 組。blog
$dp[i][j]$ 表示到達目前這個棋子的最短路,那麼轉移方程爲 $dp[i][j] = min(dp[i][j],dp[x][y]+|x-i|+|y-j|)$,其中 $(x,y)$ 爲上一組中全部棋子的座標。隊列
而後要是直接暴力的狀態轉移的話,是要TLE的,考慮進行優化。ci
考慮一個界限 $K$,假設當前組爲 $T[i]$,上一組爲 $T[i-1]$,get
那麼當 $T[i].size \le K$ 時,咱們就用繼續用上面的暴力動態轉移,那麼對於全部的「上一組」點數加起來不會差過 $nm$,所以總時間複雜度 $O(K \cdot nm)$;it
若是 $T[i].size > K$,咱們在網格上進行多源點的優先隊列BFS(或者說優先隊列dijkstra),源點是全部的 $T[i-1]$ 組內的點,搜出到全部 $T[i]$ 組內的點的最短距離,這樣BFS最多跑一遍全部網格,時間複雜度 $O(nm \log(nm))$;因爲這樣的組數目不會超過 $\frac{nm}{K}$ 個,因此總時間複雜度爲 $O(\frac{nm}{K} nm \log(nm) )$。
這樣一來,兩種加起來的總時間複雜度就是 $O(Knm+\frac{nm}{K}nm\log(nm) ) = O( nm (K + \frac{nm\log(nm)}{K}) )$,由此可知取 $K=\sqrt{nm \log(nm) }$ 時,時間複雜度最小,爲 $O(nm\sqrt{nm\log(nm)})$。
AC代碼:
#include<bits/stdc++.h> #define idx(x,y) ((x-1)*m+y) #define mp(x,y) make_pair(x,y) #define fi first #define se second using namespace std; typedef pair<int,int> P; const int INF=0x3f3f3f3f; const int maxn=305; int n,m,p,ed,K; P pos[maxn*maxn]; vector<int> T[maxn*maxn]; int dp[maxn*maxn]; int dist(const P& u,const P& v) { return abs(u.fi-v.fi)+abs(u.se-v.se); } int dx[4]={1,0,-1,0}; int dy[4]={0,1,0,-1}; int d[maxn*maxn]; bool vis[maxn*maxn]; priority_queue< P, vector<P>, greater<P> > Q; int main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin>>n>>m>>p; K=sqrt(n*m*log2(n*m)); for(int i=1,tp;i<=n;i++) { for(int j=1;j<=m;j++) { cin>>tp; T[tp].push_back(idx(i,j)); pos[idx(i,j)]=mp(i,j); if(tp==p) ed=idx(i,j); } } memset(dp,0x3f,sizeof(dp)); for(auto v:T[1]) dp[v]=dist(mp(1,1),pos[v]); for(int i=2;i<=p;i++) { if(T[i].size()<=K) { for(auto v:T[i]) for(auto u:T[i-1]) dp[v]=min(dp[v],dp[u]+dist(pos[u],pos[v])); } else { memset(d,0x3f,sizeof(d)); memset(vis,0,sizeof(vis)); for(auto u:T[i-1]) d[u]=dp[u], Q.push(mp(d[u],u)); while(Q.size()) { int u=Q.top().se; Q.pop(); if(vis[u]) continue; vis[u]=1; for(int k=0;k<4;k++) { if(pos[u].fi+dx[k]<1 || pos[u].fi+dx[k]>n) continue; if(pos[u].se+dy[k]<1 || pos[u].se+dy[k]>n) continue; int v=idx(pos[u].fi+dx[k],pos[u].se+dy[k]); if(vis[v]) continue; if(d[v]>d[u]+1) d[v]=d[u]+1, Q.push(mp(d[v],v)); } } for(auto v:T[i]) dp[v]=d[v]; } } cout<<dp[ed]<<endl; }