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到這個點距離和,那麽就能得到這個理想的復雜度了。這就要求我們能夠用不高於rowcolumn的復雜度預處理出所有的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