本题应用贪心算法
一、首先应当将问题转化为给定雷达半径d,小岛坐标 ,计算出以小岛为圆心,d为半径的圆能辐射的海岸区间,如下图所示:
二、将所有的小岛都映射到x坐标上。得下图模式:
将每个小岛映射的信息存到一个结构体中,
struct island
{
double x,y; //x,y坐标
double s,f; //s为开始坐标,f为结束坐标
};
然后对这些小岛按区间开始坐标升序排列。
可以应用贪心算法的理论依据:
1、先做出一个选择,得到一个子问题;
2、由于最左边的小岛(红色区间)必然要被一个雷达覆盖,因此必然有个雷达在A~E区间内,做出这个贪心选择后,剩下的子问题同样有个“最左边的小岛”,即具有同样的结构性质。
满足:将做出的贪心选择和子问题的最优解合并后可得到整个问题的最优解。
三、具体操作:
按小岛区间开始坐标升序排列后,从左往右,对于第一个小岛(红色区间),因为我们要求最少放多少个雷达,所以雷达放置的位置应该尽可能多的覆盖到多个小岛,对于第一个小岛的覆盖区间A~E,当然是放在E处能尽可能多得覆盖到小岛啦,于是先将雷达放在E处,继续观察第二个小岛(蓝色区间),发现蓝色区间也覆盖了E点,那么OK。继续观察第三个小岛(绿色区间),发现E并不在绿色区间内,于是需要将雷达位置左移动,移动到绿色区间结束的位置,即点D。之后再看下一个小岛的区间起始是否在D点左边。若是,则继续此类观察,若否则一次搜索结束。具体见代码。
四、代码
我在网上找了很多测试用例都能通过,但是就是无法通过poj官方测试。欢迎大家指出我程序中的问题!
#include<iostream>
#include<math.h>
using namespace std;
struct island
{
double x,y; //x,y坐标
double s,f; //s为开始坐标,f为结束坐标
};
int cmp(const void* a, const void* b)
{
island x1 = *(island*)a;
island x2 = *(island*)b;
return (x1.s) - (x2.s);
}
int main()
{
int n;
double d; // 半径
int i;
double offset;
island islands[1000];
int num = 1;
while(true)
{
cin>>n>>d;
if(0 == n && 0 == d)
break;
bool flag = true;
for(i = 0; i < n ; i++)
{
cin>>islands[i].x>>islands[i].y;
if(islands[i].y > d || islands[i].y < 0 || d <= 0)
{
flag = false;
}
if(true)
{
offset = pow(d*d-islands[i].y*islands[i].y,0.5);
islands[i].s = islands[i].x - offset;
islands[i].f = islands[i].x + offset;
}
}
if(!flag)
{
cout<<"Case "<<num++<<": "<<-1<<endl;
continue;
}
qsort(islands,n,sizeof(island),cmp);
int count = 0;
for(i = 0 ; i < n ; i++)
{
int m = i+1;
//cout<<"the: "<<i<<endl;
count ++;
if(m >= n)
break;
double finish = islands[i].f;
while(islands[m].s <= finish)
{
if(islands[m].f < finish)
finish = islands[m].f;
m++;
}
i = m-1;
}
cout<<"Case "<<num++<<": "<<count<<endl;
}
//system("pause");
return 0;
}