【Leetcode】28场双周赛 安排邮筒

Leetcode双周赛28 安排邮筒问题

       问题内容:给你一个房屋数组houses 和一个整数 k ,其中 houses[i] 是第 i 栋房子在一条街上的位置,现需要在这条街上安排 k 个邮筒。 1 < = h o u s e s . l e n g t h < = 100 , 1 < = h o u s e s [ i ] < = 1 0 4 , 1 < = k < = n 1<=houses.length<=100,1<=houses[i]<=10^4,1<=k<=n 1<=houses.length<=100,1<=houses[i]<=104,1<=k<=n,且houses中数字各不相同。

       请你返回每栋房子与离它最近的邮筒之间的距离的 最小 总和。

       答案保证在 32 位有符号整数范围以内。

示例1:

输入:houses = [1,4,8,10,20], k = 3
输出:5
解释:将邮筒分别安放在位置 3920 处。
每个房子到最近邮筒的距离和为 |3-1| + |4-3| + |9-8| + |10-9| + |20-20| = 5

示例2:

输入:houses = [2,3,5,12,18], k = 2
输出:9
解释:将邮筒分别安放在位置 314 处。
每个房子到最近邮筒距离和为 |2-3| + |3-3| + |5-3| + |12-14| + |18-14| = 9

       总结下来就是直线上n个点,安排k个点,使他们到最近点的距离之和最短。开始在想是不是要搜索或者二分,因为第三题已经是dp了。。没想到,然后发现是个dp问题,就是在n个点里插k个点的模型。

       容易想到dp[i][j]表示0-i范围内插j个邮筒。然后穷举分割点k,即最后一段k+1~j公用一个邮筒,之前的问题就变成了dp[k][j-1]。这样做的复杂度是 O ( n 3 ) O(n^3) O(n3)的,看看数据量,100,OK正好。

       现在关键是要求k+1~j范围内插一个邮筒怎么插?转化一下也就是,直线上有n个点,安排一个位置使得到这n个点的距离之和最短。这个模型就简单了,似乎是个小学的题。结论是,当有奇数个点的时候,选择中间的点;当有偶数个点,选中间俩都可以。如果硬要说为什么,可以简单证明一下。n是奇数时,假设现在已经选在中间,向左移动 Δ x \Delta x Δx,有 n + 1 2 \frac{n+1}{2} 2n+1个点距离增加了 Δ x \Delta x Δx n − 1 2 \frac{n-1}{2} 2n1个点距离减少了 Δ x \Delta x Δx。向右移动同理。或者更直观的看法,看成一对一对点的距离之和,中间那个点的距离为0时总的和最小。 n是偶数时,运用这种方法显然可证在中位数两个点之间距离之和不变。

       所以产生的附加问题我们解决了,现在就可以解决大问题了。思路就是,先预处理得到dist[i][j]表示i~j范围内建一个邮筒造成的总距离,然后运用上面的递推方程,更新dp[i][k]。代码如下。

class Solution {
public:
    int minDistance(vector<int>& houses, int k) {
        int n=houses.size();
        int dist[n][n];
        int dp[n][k+1];
        memset(dist,0,sizeof(dist));
        sort(houses.begin(),houses.end());//别忘了排序
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                dist[i][j]=dist[i][j-1]+abs(houses[j]-houses[(i+j)/2]);
            }
        }//i到j放一个邮筒
        for(int i=0;i<n;i++){
            dp[i][1]=dist[0][i];
            if(i+1<=k) dp[i][i+1]=0;
        }//初始化
        for(int j=2;j<=k;j++){
            for(int i=j;i<n;i++){
                dp[i][j]=INT_MAX;
                for(int kk=j-2;kk<i;kk++){
                    dp[i][j]=min(dp[i][j],dp[kk][j-1]+dist[kk+1][i]);
                }
            }
        }//递推方程,注意边界点,当时kk的边界点写错了一个wa了好久
        return dp[n-1][k];
    }
};

你可能感兴趣的:(动态规划)