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
  • 相关阅读:
    C# 线程手册 第二章 .NET 中的线程系列
    C# 线程手册 第四章 线程设计原则
    C# 线程手册 第四章 线程设计原则 MTA 线程模型
    C# 线程手册 第三章 使用线程 创建线程安全的包装器(实战篇)
    C# 线程手册 第一章 线程定义系列
    C# 线程手册 第三章 使用线程 小心死锁
    C# 线程手册 第四章 线程设计原则 线程及线程间关系
    C# 线程手册 第一章 线程定义 中断和局部线程存储
    C# 线程手册 第三章 使用线程 实现一个数据库连接池(实战篇)
    C# 线程手册 第四章 线程设计原则 对等线程模型
  • 原文地址:https://www.cnblogs.com/heimao5027/p/5810438.html
Copyright © 2011-2022 走看看