zoukankan      html  css  js  c++  java
  • 1030. 距离顺序排列矩阵单元格

    1030. 距离顺序排列矩阵单元格

    给出 R 行 C 列的矩阵,其中的单元格的整数坐标为 (r, c),满足 0 <= r < R 且 0 <= c < C。

    另外,我们在该矩阵中给出了一个坐标为 (r0, c0) 的单元格。

    返回矩阵中的所有单元格的坐标,并按到 (r0, c0) 的距离从最小到最大的顺序排,其中,两单元格(r1, c1) 和 (r2, c2) 之间的距离是曼哈顿距离,|r1 - r2| + |c1 - c2|。(你可以按任何满足此条件的顺序返回答案。)

     

    示例 1:

    输入:R = 1, C = 2, r0 = 0, c0 = 0
    输出:[[0,0],[0,1]]
    解释:从 (r0, c0) 到其他单元格的距离为:[0,1]
    示例 2:

    输入:R = 2, C = 2, r0 = 0, c0 = 1
    输出:[[0,1],[0,0],[1,1],[1,0]]
    解释:从 (r0, c0) 到其他单元格的距离为:[0,1,1,2]
    [[0,1],[1,1],[0,0],[1,0]] 也会被视作正确答案。
    示例 3:

    输入:R = 2, C = 3, r0 = 1, c0 = 2
    输出:[[1,2],[0,2],[1,1],[0,1],[1,0],[0,0]]
    解释:从 (r0, c0) 到其他单元格的距离为:[0,1,1,2,2,3]
    其他满足题目要求的答案也会被视为正确,例如 [[1,2],[1,1],[0,2],[1,0],[0,1],[0,0]]。
     

    提示:

    1 <= R <= 100
    1 <= C <= 100
    0 <= r0 < R
    0 <= c0 < C

     

    解法一:直接排序数组

    显然,最暴力的解法是按照距离排序,然后依次输出坐标。

    注意:

    本解法可以使用哈希表优化,即使用坐标作 key,使用距离作 value,然后按照距离排序,这样就不会因为多次对同一下标进行比较而重复计算距离
    无论如何优化,核心仍然是直接排序,时间复杂度不会优于 O(RClog(R*C))

     

    代码:

    #include<iostream>
    #include<bits/stdc++.h>
    using namespace std;
    
    struct node{
        int x;
        int y;
        int distance;
    };
    node  arr[100];
    int cmp(const node& a,const node& b){
        return a.distance<b.distance;
    }
    int main(){
        int R,C,t=0;
        cin>>R>>C;
        int r0,c0;
        cin>>r0>>c0;
        for (int i = 0; i < R; i++)
        {
            for (int j = 0; j < C; j++)
            {
                arr[t].x=i;
                arr[t].y=j;
                arr[t++].distance=abs(i-r0)+abs(j-c0);
            }
        }
        sort(arr,arr+t,cmp);
        for (int i = 0; i < t; i++)
        {
            cout<<"x= "<<arr[i].x<<" y= "<<arr[i].y<<" distance= "<<arr[i].distance<<endl; 
        }
    }

    输入:

    2 3
    1 2

    输出:

    x= 1 y= 2 distance= 0
    x= 0 y= 2 distance= 1
    x= 1 y= 1 distance= 1
    x= 0 y= 1 distance= 2
    x= 1 y= 0 distance= 2
    x= 0 y= 0 distance= 3

     

    解法二:桶排序

    遍历所有坐标,按照距离的大小分组,每组的距离相等(即放入一个桶中)
    按照距离从小到大的原则,遍历所有桶,并输出结果
    本解法关键在于求得可能的最大距离,即行距离和列距离都最大时:max(r0, R - 1 - r0) + max(c0, C - 1 - c0)

    注意:

    此解法时间复杂度为 O(R*C),理论上已达到最快可能
    实际时间消耗会比预估要差,不同语言便利程度和优化不一,原因如下:
    桶的制作涉及大量容器的初始化和存取
    桶中要存储大量的坐标信息,不论是直接使用长度为 2 的小数组存储,还是用新的简单数据类,都会耗费很多时间

     

    vector<vector<int> > allCellsDistOrder(int R, int C, int r0, int c0) {
        vector<vector<int> > ans(R * C, vector<int>(2, 0));
        vector<vector<int> > m(R + C);
        for (int i = 0; i < R; ++i) 
            for (int j = 0; j < C; ++j) {
                int dis = abs(i - r0) + abs(j - c0);
                m[dis].push_back(i);
                m[dis].push_back(j);
            }
    
        // for (int i = 0; i < R + C; ++i) {
        //     for (int j = 0; j < m[i].size(); j += 2)
        //     {
        //         cout<<m[i][j]<<" "<<m[i][j+1]<<endl;
        //     }
        // }  
        int cnt = 0;
        for (int i = 0; i < R + C; ++i) {
            for (int j = 0; j < m[i].size(); j += 2)
    
            {
                ans[cnt][0] = m[i][j];
                ans[cnt][1] = m[i][j + 1];
                ++cnt;
            }
        }
        // for (int i = 0; i < cnt; i++)
        // {
        //     cout<<ans[i][0]<<" "<<ans[i][1]<<endl;
        // }
        return ans;
    }

    解法三:BFS

    可以把所有的坐标看作树的结点,距离相等的结点位于树的同一层
    而对于每一层的结点,它们的距离 dist 可以分为行距离和列距离,且 rowDist + colDist = dist 必然成立
    使 rowDist 从 0 到 dist 递增,colDist 相应有不同的值,可以得到不同的坐标:
    横坐标为:r0 - rowDist 或 r0 + rowDist
    纵坐标为:c0 - colDist 或 c0 + colDist
    注意特殊情况:rowDist 或 colDist 为 0 时,每组只有一个正确值
    对步骤 3 中,所有在矩阵范围内的坐标进行记录
    注意:

    此解法不关心最大距离,只要步骤 4 中记录的结果达到 R * C 的数量就可以终止搜索
    此解法的时间复杂度是 O((R+C)^2),因为对每一种距离 dist,rowDist 都要进行从 0 开始递增到 dist 的遍历操作,而距离可能的最大值为 R + C
    此解法时间复杂度大于 O(R * C) 的原因是:每种距离可能产生多个不在矩阵内的坐标,但搜索算法必须依次检查予以排除
    理论上此解法并不比桶排序优秀,但是代码中极少创建额外的容器和对象,所以实际的运行效率不会太差

    class Solution {
        public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
            int[][] re = new int[R * C][2];
            int dist = 0;
            int cnt = 0;
            int[] factor = {-1, 1};
            while (cnt < R * C) {
                for (int rowDist = 0; rowDist <= dist; rowDist++) {
                    int colDist = dist - rowDist;
                    for (int i = 0; i < 2; i++) {
                        int row = r0 + factor[i] * rowDist;
                        for (int j = 0; j < 2; j++) {
                            int col = c0 + factor[j] * colDist;
                            if (row >= 0 && row < R && col >= 0 && col < C) {
                                re[cnt][0] = row;
                                re[cnt][1] = col;
                                cnt++;
                            }
                            if (colDist == 0) break;
                        }
                        if (rowDist == 0) break;
                    }
                }
                dist++;
            }
    
            return re;
        }
    }

    解法四:几何法(类 BFS)


    如果把矩阵当作二维直角坐标系中的图形,而且把所有不在矩阵内的点也考虑进来,那么所有到 (r0, c0) 点的“距离”相等的整数坐标有明显的规律:


    可以看到,它们的坐标都在一个正方形的边上(包括顶点),而且正方形的上下顶点 row 值为 r0,左右顶点 col 值为 c0。
    这样,只要保证每次找到一个正方形的顶点,然后按照规律“画出”这个正方形即可,画图步骤如下:

    保存 4 个向量标明画线的方向
    出发点为 (r0 - 1, c0)
    按照 1 中的向量指示方向画线,遇到一个正方形的顶点就更换为下一个向量(向左转 90°)
    在上述的画线步骤中,不断检查线上的整数坐标,如果符合要求就进行记录。

    注意:

    顶点的判断方法有两组,分别对应和 r0 或 c0 是否相等
    对每个距离 dist 都要画出正方形检查,检查的点数量是 8 * dist,而最大距离可能是 R + C,所以时间复杂度为 O((R+C)^2)
    此解法代码中看似没有按照距离分层遍历,实际每个初始顶点的求解过程中已经包含了按照距离分层的想法,实际极其类似 BFS
    此解法要检查的点理论上多于 BFS,尤其是 (r0, c0) 位于矩阵一角时会明显偏慢(最后要画很多很大的正方形)

    class Solution {
        public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
            int[][] re = new int[R * C][2];
            re[0][0] = r0;
            re[0][1] = c0;
            int[] dr = {1, 1, -1, -1};
            int[] dc = {1, -1, -1, 1};
            int row = r0;
            int col = c0;
            var cnt = 1;
            while (cnt < R * C) {
                row--;
                for (int i = 0; i < 4; i++) {
                    while ((i % 2 == 0 && row != r0) || (i % 2 != 0 && col != c0)) {
                        if (row >= 0 && row < R && col >= 0 && col < C) {
                            re[cnt][0] = row;
                            re[cnt][1] = col;
                            cnt++;
                        }
                        row += dr[i];
                        col += dc[i];
                    }
                }
            }
            return re;
        }
    }

     

    因上求缘,果上努力~~~~ 作者:每天卷学习,转载请注明原文链接:https://www.cnblogs.com/BlairGrowing/p/13462275.html

  • 相关阅读:
    我的第一个可用的Windows驱动完成了
    据说是一种很古老的方法
    起一卦,测今天工作,问题不少
    起一卦,找房子,马上没房子住了
    哈哈哈哈,我竟然发现了个MSDN里面的笔误
    起一卦,看现在我的工程进度怎么样。
    起卦帮同学看工作,应了。
    2012年10月17日帮朋友算得第一卦
    2013年1月13日帮朋友测的第二卦,有些地方没看出来
    bzoj2588 Spoj 10628. Count on a tree
  • 原文地址:https://www.cnblogs.com/BlairGrowing/p/13462275.html
Copyright © 2011-2022 走看看