Codeforces 677D - Vanya and Treasure - [DP+優先隊列BFS]

題目連接: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;
}
相關文章
相關標籤/搜索