贪心算法解决最少圆覆盖最多点问题

     贪心算法解决最少圆覆盖最多点问题  

     海面上有一些船需要与陆地进行通信,需要在海岸线上布置一些基站。现将问题抽象为,在x轴上方,给出N条船的坐标 ,在x轴上安放的基站可以覆盖半径为d的区域内的所有点,问在x轴上至少要安放几个点才可以将x轴上方的点都覆盖起来。试设计一个算法求解该问题,并分析算法的正确性。


解:

设计思路:

首先将所有的点按横坐标升序排序。

点集非空时,每次取出横坐标最小的点,将该点视做左半圆周上的点,取距离该点横坐标为d的x轴上的点作为圆心,从圆心处以d为半径作圆(圆心坐标在该点坐标右侧),然后去除掉包含在该圆内的点。然后继续选出剩下点中横坐标最小的点,重复以上操作,直至将所有点包括到圆中,表示所有点都被覆盖,此时得出的圆的个数就是该问题的最优解。

先把点按X轴排序 求出能覆盖每条船的点在X轴上的区间。从最左边的点开始,如果下一个点的左区间比现在的右区间还大,就要新的基站;如果下一个的右区间小于现在的右区间,需要将现在的右区间更新为小的,因为必须覆盖所有点。

如果新圆心的坐标在前一个圆心坐标左侧,那么就舍弃前一个圆心坐标,相反,则表明该圆心成立,通过这种方法,可以使一个圆尽可能覆盖周围的点,局部最优,重复每次操作,则满足全局最优

最优子结构性质:


贪心选择性:


// Test.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct Node
{
    double l;//最左可被覆盖的坐标
    double r;//最右可被覆盖的坐标
};
Node a[1010];
int i,j,k,n,m;
double d,x,y;
int ans;//结果
const double ZERO=1e-8;//控制精度


bool cmp(Node a,Node b)//最左可被覆盖的坐标排序,从左到右排序
{
    if (a.l<b.l) return 1;
    return 0;
}

void Greedy()
{
    double now=a[0].r;
    ans++;
    for (int i=1;i<n;i++)
    {
        if (a[i].l>now+ZERO)//下个点的最左被覆盖的坐标大于当前最右可被覆盖坐标
        {
            ans++;
            now=a[i].r;
        }else if (a[i].r<now+ZERO)//下个点的最左被覆盖的坐标小于当前最右可被覆盖坐标
        {
            now=a[i].r;
        }

    }
}

int main()
{
    printf("输入覆盖半径d:\n");
    cin>>d;
    printf("输入船的数量n:\n");
    cin>>n;
    printf("输入n条船的坐标:\n");

    for(int i=0;i<n;i++)
    {
        cin>>x>>y;
        double len=sqrt((double)(d*d-y*y));//勾股定理
        a[i].l=x-len;//计算最左可被覆盖的坐标
        a[i].r=x+len;//计算最右可被覆盖的坐标
    }
    sort(a,a+n,cmp);
    ans=0;
    Greedy();
    printf("在x轴上至少要安放%d个点才可以将所有点覆盖\n",ans);
    return 0;
}






  • 5
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值