zoukankan      html  css  js  c++  java
  • 【12.78%】【codeforces 677D】Vanya and Treasure

    time limit per test1.5 seconds
    memory limit per test256 megabytes
    inputstandard input
    outputstandard output
    Vanya is in the palace that can be represented as a grid n × m. Each room contains a single chest, an the room located in the i-th row and j-th columns contains the chest of type aij. Each chest of type x ≤ p - 1 contains a key that can open any chest of type x + 1, and all chests of type 1 are not locked. There is exactly one chest of type p and it contains a treasure.

    Vanya starts in cell (1, 1) (top left corner). What is the minimum total distance Vanya has to walk in order to get the treasure? Consider the distance between cell (r1, c1) (the cell in the row r1 and column c1) and (r2, c2) is equal to |r1 - r2| + |c1 - c2|.

    Input
    The first line of the input contains three integers n, m and p (1 ≤ n, m ≤ 300, 1 ≤ p ≤ n·m) — the number of rows and columns in the table representing the palace and the number of different types of the chests, respectively.

    Each of the following n lines contains m integers aij (1 ≤ aij ≤ p) — the types of the chests in corresponding rooms. It’s guaranteed that for each x from 1 to p there is at least one chest of this type (that is, there exists a pair of r and c, such that arc = x). Also, it’s guaranteed that there is exactly one chest of type p.

    Output
    Print one integer — the minimum possible total distance Vanya has to walk in order to get the treasure from the chest of type p.

    Examples
    input
    3 4 3
    2 1 1 1
    1 1 1 1
    2 1 1 3
    output
    5
    input
    3 3 9
    1 3 5
    8 9 7
    4 6 2
    output
    22
    input
    3 4 12
    1 2 3 4
    8 7 6 5
    9 10 11 12
    output
    11

    【题解】

    分成p-1步进行最短路;
    即从所有i号箱子所在的位置开始进行bfs->最短路;这里最短路的终点是所有的i+1号箱子;
    用一个dp[i][j]表示到(i,j)且刚好打开(i,j)的箱子所需要的最短路;
    每次用得到的i号箱子到其他点的最短路来更新第i+1号箱子的dp[x]y]即可.
    但是如果直接这样的话肯定不行的;
    O(n*m*广搜一个n*m的图);显然会超时- -
    毕竟9e4*9e4呢;
    这里用到了分类讨论的思想
    for (int i = 1;i <=p-1;i++)
    {
    —- if (pos[i].size*pos[i+1].size<=n*m)
    ———则直接用枚举来搞i+1号
    —-else
    ——-用广搜更新i+1号

    }
    更具体点

        for (int k = 1; k <= p - 1; k++)//枚举当前的起点是啥.
        {
            int len1 = pos[k].size(), len2 = pos[k + 1].size();
            if (len1*len2 <= n*m)//如果后一个箱子的点的数目乘当前箱子的点的数目小于等于n*m,则会比直接广搜好一点(可能)
            {//要想到如果len1=2,len2=2,而n=300,m=300;那么速度不是快一点了
                for (int i = 0; i <= len1 - 1; i++)
                {
                    int a1 = pos[k][i].first, b1 = pos[k][i].second;//取出前一个点的第i号箱子的坐标
                    for (int j = 0; j <= len2 - 1; j++)//取出后一个箱子的第j号点的坐标
                    {
                        int a2 = pos[k + 1][j].first, b2 = pos[k + 1][j].second;
                        if (dis[a2][b2] > dis[a1][b1] + abs(a2 - a1) + abs(b2 - b1))//如果能够更新最优解则更新
                            dis[a2][b2] = dis[a1][b1] + abs(a2 - a1) + abs(b2 - b1);//相当于不是一步一步地广搜了。直接走到下一个点。
                    }
                }
            }
            else
            //否则就直接进行普通的广搜


    为什么这样会更快?
    假设p=n*m/2;(且n和m都是最大数据300)
    则最坏情况每个点都只有两个(我知道p只有一个!)
    则大家可以看看循环;
    每一个判断都会直接进入2*2的枚举;
    然后n*m/2也不大就只有45000;
    2*2*45000肯定没问题的;
    再坏一点
    p=n*m/100
    n和m依然是最大数据300
    最坏情况是每个点都有100个
    100*100<=90000
    所以依然暴力枚举
    O(100*100*p)
    =O(100*100*90000/100)
    =O(100*100*900)
    也就是900万;
    也是可以接受的;
    再坏
    p=n*m/1000
    则最坏情况每个点最坏出现1000次
    1000*1000>90000
    则每个点都直接用普通的最短路搞->因为直接暴力搞显然不合适;
    复杂度大概就是
    O(300*300*300*300/1000)
    =O(9W*900);
    =8100W;
    再坏
    p=n*m/10000
    则最坏情况每个点最坏出现10000次
    10000*10000>90000
    则每个点都直接用普通的最短路搞
    复杂度大概就是
    O(300*300*300*300/10000)
    =O(9W*900);
    =810W;
    可以看到复杂度会变小。
    所以这个分类的做法是有对算法进行优化的;
    主要就是当p非常大的时候,对应每个点的出现次数会相应地减少。
    这里就可以避免每次都进行最短路。
    直接暴力枚举反而更快;

    #include <cstdio>
    #include <vector>
    #include <queue>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int MAXN = 310;
    const int INF = 0x3f3f3f3f;
    const int MAXP = 100000;
    const int dx[5] = { 0,0,0,1,-1 };
    const int dy[5] = { 0,1,-1,0,0 };
    
    int n, m, p;
    vector < pair<int, int> > pos[MAXP];
    int dis[MAXN][MAXN],tempdis[MAXN][MAXN];
    queue < pair<int, int> >dl;
    bool inque[MAXN][MAXN];
    
    void input(int &r)
    {
        r = 0;
        char t = getchar();
        while (!isdigit(t)) t = getchar();
        int sign = 1;
        if (t == '-')sign = -1;
        while (!isdigit(t)) t = getchar();
        while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
        r = r*sign;
    }
    
    int main()
    {
        //freopen("F:\rush.txt", "r", stdin);
        input(n); input(m); input(p);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                dis[i][j] = INF;
        for (int i = 1;i <= n;i++)
            for (int j = 1; j <= m; j++)
            {
                int key;
                input(key);
                pos[key].push_back(make_pair(i, j));
                if (key == 1)
                    dis[i][j] = abs(i - 1) + abs(j - 1);
            }
        for (int k = 1; k <= p - 1; k++)
        {
            int len1 = pos[k].size(), len2 = pos[k + 1].size();
            if (len1*len2 <= n*m)
            {
                for (int i = 0; i <= len1 - 1; i++)
                {
                    int a1 = pos[k][i].first, b1 = pos[k][i].second;
                    for (int j = 0; j <= len2 - 1; j++)
                    {
                        int a2 = pos[k + 1][j].first, b2 = pos[k + 1][j].second;
                        if (dis[a2][b2] > dis[a1][b1] + abs(a2 - a1) + abs(b2 - b1))
                            dis[a2][b2] = dis[a1][b1] + abs(a2 - a1) + abs(b2 - b1);
                    }
                }
            }
            else
            {
                for (int i = 1; i <= n; i++)
                    for (int j = 1; j <= m; j++)
                        tempdis[i][j] = INF;
    
                for (int i = 1; i <= n; i++)
                    for (int j = 1; j <= m; j++)
                        inque[i][j] = false;
    
                int len = pos[k].size();
                for (int i = 0; i <= len - 1; i++)
                {
                    dl.push(pos[k][i]);
                    int x = pos[k][i].first, y = pos[k][i].second;
                    inque[x][y] = true;
                    tempdis[x][y] = dis[x][y];
                }
    
                while (!dl.empty())
                {
                    pair <int, int> p = dl.front();
                    dl.pop();
                    inque[p.first][p.second] = false;
                    for (int i = 1; i <= 4; i++)
                    {
                        int x = p.first + dx[i], y = p.second + dy[i];
                        if (x <1 || x>n || y<1 || y>m)
                            continue;
                        if (tempdis[x][y] > tempdis[p.first][p.second] + abs(x - p.first) + abs(y - p.second))
                        {
                            tempdis[x][y] = tempdis[p.first][p.second] + abs(x - p.first) + abs(y - p.second);
                            if (!inque[x][y])
                            {
                                inque[x][y] = true;
                                dl.push(make_pair(x, y));
                            }
                        }
                    }
                }
    
                len = pos[k + 1].size();
                for (int i = 0; i <= len - 1; i++)
                    dis[pos[k + 1][i].first][pos[k + 1][i].second] = tempdis[pos[k + 1][i].first][pos[k + 1][i].second];
            }
        }
        printf("%d
    ", dis[pos[p][0].first][pos[p][0].second]);
        return 0;
    }
  • 相关阅读:
    halcon应用案例探究
    Halcon中模板匹配方法的总结归纳
    halcon开发必读
    全局Threshold和动态阈值分割Dyn_Threshold的应用场景
    halcon电路断裂检测
    halcon之 distance_transform
    VS调试不能进入断点,提示当前不会命中断点还未为文档加载任何符号
    .net reactor使用教程(一)——界面各功能说明(转)
    (转) 开运算opening_circle和闭运算closing_circle的异同
    Win10系统安装UEFI+GPT配置
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632129.html
Copyright © 2011-2022 走看看