Codeforces 677D - Vanya and Treasure - [DP+优先队列BFS]

题目链接:http://codeforces.com/problemset/problem/677/D

题意:

有 $n \times m$ 的网格,每个网格上有一个棋子,棋子种类为 $t[i][j]$,棋子的种类数为 $p$。

现在出发点为 $(1,1)$,必须按照种类 $1 \sim p$ 进行移动,即从种类 $x$ 的棋子出发,下一个目标必须是 $x+1$ 才行,直到走到种类为 $p$ 的棋子就终止。求最短路径。

题解:

我们先把棋子按照种类分组,分成 $p$ 组。

$dp[i][j]$ 表示到达目前这个棋子的最短路,那么转移方程为 $dp[i][j] = min(dp[i][j],dp[x][y]+|x-i|+|y-j|)$,其中 $(x,y)$ 为上一组中所有棋子的坐标。

然后要是直接暴力的状态转移的话,是要TLE的,考虑进行优化。

考虑一个界限 $K$,假设当前组为 $T[i]$,上一组为 $T[i-1]$,

那么当 $T[i].size \le K$ 时,我们就用继续用上面的暴力动态转移,那么对于所有的“上一组”点数加起来不会差过 $nm$,因此总时间复杂度 $O(K \cdot nm)$;

如果 $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;
}

猜你喜欢

转载自www.cnblogs.com/dilthey/p/10461665.html