574.邮局的建立(没做)

题目

给出一个二维的网格,每一个格子上用 1 表示房子,0 表示空。要求在网格中,找到一个空地建立邮局,使得邮局到所有的房子的距离和最小。返回所有房子到邮局的最小距离和,如果不可能建邮局则返回-1。

注意事项

你可以穿越房子和空地
你只能在空地建立邮局。
您在真实的面试中是否遇到过这个题? Yes

样例

给出一个网格

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

返回 6 (把邮局设立在(1,1)这个位置时,邮局离所有的房子的距离是最近的)。

代码

// 方法一 
public class Solution {
    /**
     * @param grid a 2D grid
     * @return an integer
     */
    public int shortestDistance(int[][] grid) {
        // Write your code here
        int n = grid.length;
        if (n == 0)
            return -1;

        int m = grid[0].length;
        if (m == 0)
            return -1;

        List<Integer> sumx = new ArrayList<Integer>();
        List<Integer> sumy = new ArrayList<Integer>();
        List<Integer> x = new ArrayList<Integer>();
        List<Integer> y = new ArrayList<Integer>();

        int result = Integer.MAX_VALUE;
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                if (grid[i][j] == 1) {
                    x.add(i);
                    y.add(j);
                }
        
        Collections.sort(x);
        Collections.sort(y);

        int total = x.size();

        sumx.add(0);
        sumy.add(0);
        for (int i = 1; i <= total; ++i) {
            sumx.add(sumx.get(i-1) + x.get(i-1));
            sumy.add(sumy.get(i-1) + y.get(i-1));
        }

        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                if (grid[i][j] == 0) {
                    int cost_x = get_cost(x, sumx, i, total);
                    int cost_y = get_cost(y, sumy, j, total);
                    if (cost_x + cost_y < result)
                        result = cost_x + cost_y;
                }

        return result;
    }

    public int get_cost(List<Integer> x, List<Integer> sum, int pos, int n) {
        if (n == 0)
            return 0;
        if (x.get(0) > pos)
            return sum.get(n) - pos * n;

        int l = 0, r = n - 1;
        while (l + 1 < r) {
            int mid = l + (r - l) / 2;
            if (x.get(mid) <= pos)
                l = mid;
            else
                r = mid - 1;
        }
        
        int index = 0;
        if (x.get(r) <= pos)
            index = r;
        else
            index = l;
        return sum.get(n) - sum.get(index + 1) - pos * (n - index - 1) + 
               (index + 1) * pos - sum.get(index + 1);
    }
}

// 方法二
public class Solution {
    /**
     * @param grid a 2D grid
     * @return an integer
     */
    public int shortestDistance(int[][] grid) {
        // Write your code here
        int row = grid.length, column = grid[0].length;
        if(row == 0 || column == 0 || !haveZero(grid,row,column)) {
            return -1;
        }

        int[] rowSum = new int[row];
        int[] columnSum = new int[column]; 
        for(int i = 0; i < row; i++)
            for(int j = 0; j < column; j++)
                if(grid[i][j] == 1) {
                    rowSum[i]++;
                    columnSum[j]++;
                }

        int[] ansRow = new int[row];
        int[] ansColumn = new int[column];
        getSumDistance(rowSum,row,ansRow);
        getSumDistance(columnSum,column,ansColumn);

        int ans = Integer.MAX_VALUE;
        for(int i = 0; i < row; i++)
            for(int j = 0; j < column; j++)
                if(grid[i][j] == 0 && ans > ansRow[i] + ansColumn[j]) {
                    ans = ansRow[i] + ansColumn[j];
                }
        return ans;
    }

    void getSumDistance(int[] a,int n,int[] ans) {
        int[] prefixSum1 = new int[n];
        int[] prefixSum2 = new int[n];
        /*
        第一阶段,处理前缀。
        prefixSum1记录数组 a 的前缀和,即:prefixSum1[i]=a[0]+a[1]+..+a[i].
        prefixSum2记录数组 prefixSum1 前缀和,prefixSum2即为前 i 个点到第 i 个点的代价和。
        */
        prefixSum1[0] = a[0];
        for(int i = 1; i < n; i++) {
            prefixSum1[i] = prefixSum1[i - 1] + a[i];
        }
        prefixSum2[0] = 0;
        for(int i = 1; i < n; i++) {
            prefixSum2[i] = prefixSum2[i - 1] + prefixSum1[i - 1];
        }

        for(int i = 0; i < n; i++) {
            ans[i] = prefixSum2[i];
        }

        /*
        第二阶段,处理后缀。
        prefixSum1记录数组 a 的后缀和,即:prefixSum1[i]=a[n-1]+a[n-2]+..+a[i].
        prefixSum2记录数组 prefixSum1 的后缀和,prefixSum2即为 i 之后的点到第 i 个点的代价和。
        */
        prefixSum1[n - 1] = a[n - 1];
        for(int i = n - 2; i >= 0; i--) {
            prefixSum1[i] = prefixSum1[i + 1] + a[i];
        }
        prefixSum2[n - 1] =0;
        for(int i = n - 2; i >= 0; i--) {
            prefixSum2[i] = prefixSum2[i + 1] + prefixSum1[i + 1];
        }

        for(int i = 0; i < n; i++) {
            ans[i] += prefixSum2[i];
        }

        /*
        ans[i] 即为a数组中所有点到第 i 点的代价和
        */
    }

    boolean haveZero(int[][] grid, int row, int column) {
        for(int i = 0; i < row; i++) {
            for(int j = 0; j < column; j++){
                if(grid[i][j] == 0) {
                    return true;
                }
            }
        }
        return false;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容