codeforces div2 677 D

http://codeforces.com/problemset/problem/677/D

题目大意:

给你一个n*m的图,上面有p种钥匙(p<=n*m),每种钥匙至少有一个,期初所有为1的钥匙都是可拿的,拿到了该钥匙以后(假设该钥匙的val是v)就可以拿v+1种类的钥匙。问最后拿到第p个钥匙最少走多少步?(钥匙为p种类的只有一种)

思路:官方题解貌似是二维线段树的,不过这题可以用另外一个方法来优化。

首先假设我们目前在的颜色的c,我们要到c+1颜色的格子里面去,如果单纯的对于每种颜色c跑一次spfa的话,那么就是n*n*m*m了,所以我们分类讨论。当len(c)*len(c+1)<=n*m的时候,直接暴力就好了,反之就跑spfa。因为len(c)*len(c+1)的组数必然不多,所以肯定比每次直接跑n*m的spfa要好。

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int maxn = 300 + 5;
int n, m, p;
int atlas[maxn][maxn];
vector<pair<int, int> > v[maxn * maxn];
int dp[maxn][maxn], dis[maxn][maxn];
bool vis[maxn][maxn];
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};

int solve(){
    for (int i = 1; i < p; i++){
        int len1 = v[i].size();
        int len2 = v[i + 1].size();
        if (len1 * len2 <= n * m){
            for (int j = 0; j < len1; j++){
                int jx = v[i][j].fi, jy = v[i][j].se;
                for (int k = 0; k < len2; k++){
                    int kx = v[i + 1][k].fi, ky = v[i + 1][k].se;
                    dp[kx][ky] = min(dp[kx][ky], dp[jx][jy] + abs(kx - jx) + abs(ky - jy));
                }
            }
        }
        else {
            memset(dis, -1, sizeof(dis));
            memset(vis, false, sizeof(vis));
            queue<pair<int, int> > que;
            for (int j = 0; j < len1; j++){
                int jx = v[i][j].fi, jy = v[i][j].se;
                dis[jx][jy] = dp[jx][jy];
                que.push(mk(jx, jy));
                vis[jx][jy] = true;
            }
            while (!que.empty()){
                pair<int, int> u = que.front(); que.pop();
                vis[u.fi][u.se] = false;
                for (int j = 0; j < 4; j++){
                    int jx = u.fi + dx[j], jy = u.se + dy[j];
                    if (jx <= 0 || jx > n || jy <= 0 || jy > m) continue;
                    if (dis[jx][jy] == -1 || dis[jx][jy] > dis[u.fi][u.se] + 1){
                        dis[jx][jy] = dis[u.fi][u.se] + 1;
                        if (!vis[jx][jy]){
                            vis[jx][jy] = true;
                            que.push(mk(jx, jy));
                        }
                    }
                }
            }
            for (int j = 0; j < len2; j++){
                int jx = v[i + 1][j].fi, jy = v[i + 1][j].se;
                dp[jx][jy] =  min(dp[jx][jy], dis[jx][jy]);
            }
        }
    }
    int px = v[p][0].fi, py = v[p][0].se;
    return dp[px][py];
}

int main(){
    cin >> n >> m >> p;
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++){
            int c; scanf("%d", &c);
            if (c == 1) dp[i][j] = i + j - 2;
            else dp[i][j] = inf;
            v[c].pb(mk(i, j));
            atlas[i][j] = c;
        }
    }
    printf("%d
", solve());
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/heimao5027/p/5810438.html