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;
    }
    
}

 

 

 

 

 

posted @ 2017-10-29 12:37  jasminemzy  阅读(187)  评论(0编辑  收藏  举报