贪心证明 | 最少圆覆盖问题

问题描述

原题链接

海面上有一些船需要与陆地进行通信,需要在海岸线上布置一些基站。现将问题抽象为,在 x 轴上方,给出 N 条船的坐标 p 1 , p 2 , … , p N , p i = ( x i , y i ) , 0 ≤ y i ≤ d , 1 ≤ i ≤ N , p_1,p_2,…,p_N,p_i=(x_i,y_i),0≤y_i≤d,1≤i≤N, p1,p2,,pNpi=(xi,yi)0yid,1iN在 x 轴上安放的基站可以覆盖半径为 d 的区域内的所有点,问在 x 轴上至少要安放几个点才可以将 x 轴上方的点都覆盖起来。试设计一个算法求解该问题,并分析算法的正确性。

算法设计

将这些船按照 x 坐标从小到大进行排序。按顺序遍历排序过后的船,若当前船无法被基站覆盖,则沿 x 增加的方向建设一个新基站,使得该基站恰好能覆盖该船。

最优子结构性质

设船 P = { 1 , 2 , . . . , n } P = \{1, 2, ..., n\} P={1,2,...,n} 已按 x 坐标递增的顺序排序。设 A 是包括基站 1 的最优解,则 A’ 是除去基站 1 覆盖的船,剩余所有船构成的子问题 P‘ 的最优解,基站数 ∣ A ′ ∣ = ∣ A ∣ − 1 |A'| = |A| - 1 A=A1

A’ 中的基站能够覆盖所有的船,只需要证明 A‘ 的基站数是最少的。
假设有个更优的基站安排方案 B’,满足 ∣ A ′ ∣ > ∣ B ′ ∣ |A'| > |B'| AB,那么原问题的最优解应该是 ∣ B ′ ∣ + 1 < ∣ A ′ ∣ + 1 |B'| + 1 < |A'| + 1 B+1A+1,与 A 是最优解矛盾。

因此,问题的最优解包含子问题的最优解。

贪心选择性

设船 P = { 1 , 2 , . . . , n } P = \{1, 2, ..., n\} P={1,2,...,n} 已按 x 坐标递增的顺序排序。

假设存在一个最优解 A,其中第一个基站的位置不是贪心算法选择的位置,即不是能覆盖第一艘船的最右边的位置。那么,我们可以将这个基站的位置向右移动,直到它到达能覆盖第一艘船的最右边的位置,这样不会减少它能覆盖的船的数量。因此,这样得到的解 B 也是一个最优解,且它的第一个基站的位置是贪心算法选择的位置。

设贪心算法选择的第一个基站的位置为 x,我们将这个基站覆盖的船从原问题中去掉,得到一个子问题。对于这个子问题,我们同样可以用上述的方式得出,贪心算法选择的第一个位置是一个最优解。不断进行上述流程,我们最终可以得到船的数量为 0 的最小规模的子问题,此时不需要放置任何基站,显然是最优解。

通过上述的归纳假设,我们可以得出,贪心算法可以得到这个子问题的最优解。

代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

typedef pair<int, int> PII;

int main()
{
    int n, d;
    while(cin >> n >> d && (n || d)) {
        vector<PII> p(n);
        int x, y;
        for(int i = 0; i < n; i++) {
            cin >> x >> y;
            p[i] = {x, y};
        }
        sort(p.begin(), p.end());
        int res = 0, t;
        for(auto it : p) {
            if(res == 0 || (it.first - t)*(it.first - t) + it.second*it.second >= d*d) {
                res++;
                t = it.first + sqrt(d*d - it.second*it.second);
            }
        }
        cout << res << endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值