zoukankan      html  css  js  c++  java
  • lintcode574- Build Post Office- hard

    Given a 2D grid, each cell is either an house 1 or empty 0 (the number zero, one), find the place to build a post office, the distance that post office to all the house sum is smallest. Return the smallest distance. Return -1 if it is not possible.

    Notice

    • You can pass through house and empty.
    • You only build post office on an empty.

    Example

    Given a grid:

    0 1 0 0
    1 0 1 1
    0 1 0 0
    

    return 6. (Placing a post office at (1,1), the distance that post office to all the house sum is smallest.)

     

    基本想法:对每个0处计算到所有房子的距离,打擂台选出最近的。遍历每个0肯定是O(n2)了,所以算距离快不快很关键。本题最后用O(1)的方法算距离(如下)。

    O(n2)算距离:最粗暴的算法了。二重循环遍历所有1,用manhattan距离计算公式算。

    O(n)算距离:一开始先把1的坐标都存进List了,之后直接用manhatten距离计算公式用坐标来算。不过因为每次算要读出所有的1,还是有n的复杂度的。

    0(1)算距离:1.O(n2)记录每行房子数和每列房子数 cntRow[], cntCol[]  2.O(n2)记录每行,到其他各行所有房子的距离,的和 distRow[]。 3.O(n2)记录每列,到其他各行所有房子的距离,的和distCol[]。 4.之后对任意一个空地0,比如grid[i][j]就可以O(1)获得到所有房子的距离了:distRow[i] + distCol[j];

    其实相当于投资额外的空间,把本来很麻烦的事情拖出来,提前用好几个O(n2)时间的循环算好,存到矩阵里,这样才可能做到以后要拿距离的时候可以直接O(1)调用到。因为本来这道题下限就是O(n2),所以前面那几个预处理用的O(n2)事情不足挂齿。

    以后manhattan距离计算优化都可以想到这个,把点与多个点之间的曼哈顿距离和,拆成x上距离和 + y上距离和。 x,y上距离和又拆成 个数 * 行差。

    额外贴一个问答看到的思路分析过程。

    1.拿到一道题目,就先想想它他的理论下限复杂度是否能达到?然后据此思考解法。
    2.这道题目,读入是必须的,而读入的复杂度是rowcolumn,这个就是理论上的最低复杂度了。
    能不能达到呢?如果是这样的复杂度,基本就要求我们要把矩阵遍历常数遍就得到结果。
    假设我们遍历矩阵的时候,能够O(1)的得到所有的house到这个点距离和,那么就能得到这个理想的复杂度了。这就要求我们能够用不高于row
    column的复杂度预处理出所有的house到某一点的距离和。
    3.有关于这种在矩阵中求解曼哈顿距离的题目,一般都可以讲 x 与 y 拆分开来解决。
    比如我们要所有的house到点(1,1)的距离和,那么可以拆分成求所有的house到第一行的距离和 ansr[1] 与到第一列的距离和 ansc[1],ansr[1]+ansc[1]即为所有的house到点(1,1)的距离和。
    根据这个思路,我们只要能预处理出所有的house到某一行的距离和 ansr[] 以及到某一列的距离和 ansc[] ,即可O(1)的得到所有house到某一点的距离和。
    4.如何求所有的house到某一行的距离和?
    以样例为例:
    我们用sr[]来记录每一行有多少个house,于是得到:sr[0]=1,sr[1]=3,sr[2]=1;
    所有的house到第 i 行的距离就是:
    sum=0;
    for j = 0 to 2
    sum += abs(i-j)*sr[j];
    5.去掉上式中的abs,求所有的house到第i行就变成两个子问题,求所有行号小于 i 的house到第 i 行的距离和+所有行号大于 i 的house到第 i 行的距离和。
    对于第一个子问题:我们可以用前缀和来做到O(n)的解决。
    用s1[]来记录sr[]的前缀和,即:s1[i]=sr[0]+..sr[i]
    然后用s2[]来记录第一个子问题的答案,s2[i]=s2[i-1]+s1[i-1]。
    第二个子问题的解法与第一个子问题是相似的,只不过从前缀和变成后缀和而已。

    1.O(n2). 最终写法

    public class Solution {
        /*
         * @param grid: a 2D grid
         * @return: An integer
         */
        
        private class Point{
            int x;
            int y;
            public Point(int x, int y) {
                this.x = x;
                this.y = y;
            }
        }
        
        public int shortestDistance(int[][] grid) {
            // write your code here
            if (grid == null || grid.length == 0 || grid[0].length == 0) {
                return -1;
            }
            
            int h = grid.length;
            int w = grid[0].length;
            int[] houseCntRow = new int[h];
            int[] houseCntCol = new int[w];
            int count = 0;
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    if (grid[i][j] == 1) {
                        count++;
                        houseCntRow[i]++;
                        houseCntCol[j]++;
                    }
                }
            }
            
            // avoid all being 1
            if (count == h * w) {
                return -1;
            }
            
            int[] distSumRow = new int[h];
            int[] distSumCol = new int[w];
            
            for (int row = 0; row < h; row++) {
                for (int i = 0; i < h; i++) {
                    distSumRow[row] += Math.abs(row - i) * houseCntRow[i];
                }
            }
            
            for (int col = 0; col < w; col++) {
                for (int j = 0; j < w; j++) {
                    distSumCol[col] += Math.abs(col - j) * houseCntCol[j];
                }
            }
            
            int minDist = Integer.MAX_VALUE;
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    if (grid[i][j] == 1) {
                        continue;
                    }
                    int dist = distSumRow[i] + distSumCol[j];
                    minDist = Math.min(minDist, dist);
                }
            }
            
            return minDist;
        }
        
    }

    2.O(n3)时间复杂度的写法 (O(n)算距离):

    public class Solution {
        /*
         * @param grid: a 2D grid
         * @return: An integer
         */
        
        private class Point{
            int x;
            int y;
            public Point(int x, int y) {
                this.x = x;
                this.y = y;
            }
        }
        
        public int shortestDistance(int[][] grid) {
            // write your code here
            if (grid == null || grid.length == 0 || grid[0].length == 0) {
                return -1;
            }
            
            int h = grid.length;
            int w = grid[0].length;
            List<Point> list = new ArrayList<Point>();
            int count = 0;
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    if (grid[i][j] == 1) {
                        count++;
                        list.add(new Point(i, j));
                    }
                }
            }
            
            if (count == h * w) {
                return -1;
            }
            
            int minDist = Integer.MAX_VALUE;
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    if (grid[i][j] == 1) {
                        continue;
                    }
                    int dist = 0;
                    for (Point p : list) {
                        dist += Math.abs (i - p.x) + Math.abs(j - p.y);
                    }
                    minDist = Math.min(minDist, dist);
                }
            }
            
            return minDist;
        }
        
    }
  • 相关阅读:
    小程序动态修改页面标题setNavigationBarTitle
    webapi发布在iis之后报错Http 403.14 error
    vue调用子组件方法时,参数传不过去
    Echarts中X轴坐标太密集,分段显示
    使用echarts时,鼠标首次移入屏幕会闪动,全屏会出现滚动条
    js关于数组的操作(合并数组、添加数组、循环等)
    在vue项目中使用echarts
    npm i安装命令中的-g -D -S的区别
    ArcGIS api for JS 实现三维飞行漫游功能
    Vue通过EventBus实现兄弟组件间通信
  • 原文地址:https://www.cnblogs.com/jasminemzy/p/7749800.html
Copyright © 2011-2022 走看看