AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

LeetCode 475. Heaters    原题链接    简单

作者: 作者的头像   wzc1995 ,  2018-07-01 18:27:51 ,  所有人可见 ,  阅读 2395


6


1

题目描述

冬季已经来临。你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。

现在,给出位于一条水平线上的房屋和供暖器的位置,找到可以覆盖所有房屋的最小加热半径。

所以,你的输入将会是房屋和供暖器的位置。你将输出供暖器的最小加热半径。

说明

  • 给出的房屋和供暖器的数目是非负数且不会超过 25000。
  • 给出的房屋和供暖器的位置均是非负数且不会超过 10^9。
  • 只要房屋位于供暖器的半径内(包括在边缘上),它就可以得到供暖。
  • 所有供暖器都遵循你的半径标准,加热的半径也一样。

样例

输入:[1,2,3],[2]
输出:1
解释:仅在位置 2 上有一个供暖器。如果我们将加热半径设为 1,那么所有房屋就都能得到供暖。
输入:[1,2,3,4],[1,4]
输出:1
解释:在位置 1 和 4 上有两个供暖器。我们需要将加热半径设为 1,这样所有房屋就都能得到供暖。

算法1

(二分答案) $O(n \log n + m \log m + (n + m) \log L)$
  1. 将房屋和暖气的位置分别从小到大排序。
  2. 假设得到了加热半径 $r$,即可在 $O(n + m)$ 的时间内,判断是否所有的房屋都得到了暖气,具体为,逐一枚举房屋,然后判断是否有暖气覆盖,由于房屋和暖气的坐标都是单调的,所以第 $i+1$ 个房屋不会使用坐标小于第 $i$ 个房屋的暖气位置。
  3. 加热半径也是单调的,故可以使用二分来加速寻找最小的半径。

时间复杂度

  • 排序的时间复杂度为 $O(n \log n + m \log m)$,二分判定的时间复杂度为 $O((n + m) \log L)$,其中$L$为最大房屋或暖气的坐标。

空间复杂度

  • 仅需要常数的额外空间。

C++ 代码

class Solution {
public:

    bool check(int r, const vector<int>& houses, const vector<int>& heaters) {
        int n = heaters.size();
        for (int i = 0, j = 0; i < houses.size(); i++) {
            while (j < n && abs(houses[i] - heaters[j]) > r)
                j++;
            if (j == n)
                return false;
        }
        return true;
    }

    int findRadius(vector<int>& houses, vector<int>& heaters) {
        sort(houses.begin(), houses.end());
        sort(heaters.begin(), heaters.end());
        if (houses.size() == 0)
            return 0;

        int l = 0, r = max(*houses.rbegin(), *heaters.rbegin());
        while (l < r) {
            int mid = (l + r) >> 1;
            if (check(mid, houses, heaters))
                r = mid;
            else
                l = mid + 1;
        }

        return l;
    }
};

算法2

(贪心) $O(n \log n + m \log m)$
  1. 将房屋和暖气的位置分别从小到大排序。
  2. 逐一枚举每个房屋,对于房屋 $i$,他使用暖气必定是距离他左右(如果有)最近的两个之一,由于我们已经对坐标进行了排序,所以这个过程如算法 1 一样是单调的。
  3. 按照 2 的过程,随时更新半径的最大值$r$即可。

时间复杂度

  • 排序的时间复杂度为 $O(n \log n + m \log m)$,然后是线性扫描,每个位置最多被遍历一次,时间复杂度为 $O(n + m)$,故总时间复杂度为 $O(n \log n + m \log m)$。

空间复杂度

  • 仅需要常数的额外空间。

C++ 代码

class Solution {
public:
    int findRadius(vector<int>& houses, vector<int>& heaters) {
        int n = heaters.size();
        sort(houses.begin(), houses.end());
        sort(heaters.begin(), heaters.end());

        int r = 0;
        for (int i = 0, j = 0; i < houses.size(); i++) {
            if (abs(houses[i] - heaters[j]) > r) {

                while (j < n && heaters[j] < houses[i])
                    j++;

                if (j == n) {
                    r = max(r, houses[i] - heaters[j - 1]);
                    j--;
                } else if (j == 0) {
                    r = max(r, heaters[j] - houses[i]);
                } else {
                    if (houses[i] - heaters[j - 1] < heaters[j] - houses[i]) {
                        r = max(r, houses[i] - heaters[j - 1]);
                        j--;
                    } else
                        r = max(r, heaters[j] - houses[i]);
                }
            }
        }
        return r;
    }
};

11 评论


用户头像
sysml   2019-04-09 15:52         踩      回复
另一种思路:
int findRadius(vector<int>& houses, vector<int>& heaters) {
        sort(heaters.begin(),heaters.end());
        int res=0;
        for(const auto &h:houses){
            res=max(res,bh(heaters,h));
        }
        return res;
    }

    int bh(vector<int>& heaters, int house){
        int l=-1,r=heaters.size();
        int d=INT_MAX;
        while(l+1!=r){
            int mid=l+(r-l)/2;
            int h=heaters[mid];
            if(h<house){
                l=mid;
                d=min(d,house-h);
            }
            else{
                r=mid;
                d=min(d,h-house);
            }
        }
        return d;
    }

用户头像
lovebecky   2018-12-22 16:21         踩      回复

两个解法都很精彩,学习啦!


用户头像
小雨   2018-08-19 10:21         踩      回复

不喜欢这个题,不过我喜欢不喜欢也没用(^ ^)


用户头像
小雨   2018-08-19 09:32         踩      回复

这题都可以用二分写出来,厉害厉害!


用户头像
小雨   2018-08-19 09:32         踩      回复

第i+1个房屋不会使用坐标小于第i个房屋的暖气位置?哇,想想还挺绕的


用户头像
小雨   2018-08-19 09:30         踩      回复

我感觉这个简单题一点都不简单嘛,可能是这类题做得太少了。


用户头像
小雨   2018-08-19 09:28         踩      回复

这里为什么是贪心算法呢?为什么可以用贪心算法呢?我搞不太懂什么时候可以用贪心算法。

用户头像
yxc   2018-08-20 17:42         踩      回复

贪心只是一种策略的统称,具体贪心的方式有很多种。
这道题目中每个房屋使用的暖气必是左右相邻两个暖气之一,这就是贪心思想的体现。

用户头像
小雨   2018-08-20 23:25    回复了 yxc 的评论         踩      回复

贪心策略,有没有总结贪心策略的板块哇,我觉得那个二分的模板真是总结得太精辟了,短短的几行代码抓住了二分的精髓。

用户头像
yxc   2018-08-20 23:27    回复了 小雨 的评论         踩      回复

目前还没写,之后有时间会总结一下hh


用户头像
小雨   2018-08-19 09:07         踩      回复

每次碰到很有新意的题目,而且如果题目还特别长的话都不想往下看,但是因为有这个网站在这里,就可以看下去。看来学习还是交互式的好哇。^_^


App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息