1. 程式人生 > >lintcode574- Build Post Office- hard

lintcode574- Build Post Office- hard

ron ive 能夠 ndis find build 遍歷 lin 計算

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

lintcode574- Build Post Office- hard