zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    NTP on FreeBSD 12.1
    Set proxy server on FreeBSD 12.1
    win32 disk imager使用后u盘容量恢复
    How to install Google Chrome Browser on Kali Linux
    Set NTP Service and timezone on Kali Linux
    Set static IP address and DNS on FreeBSD
    github博客标题显示不了可能是标题包含 特殊符号比如 : (冒号)
    server certificate verification failed. CAfile: none CRLfile: none
    删除文件和目录(彻底的)
    如何在Curl中使用Socks5代理
  • 原文地址:https://www.cnblogs.com/heimao5027/p/5810438.html
Copyright © 2011-2022 走看看