poj 1328 贪心算法的深入剖析
来源:互联网 发布:淘宝客推广视频 编辑:程序博客网 时间:2024/05/15 00:41
题目链接:http://acm.pku.edu.cn/JudgeOnline/problem?id=1328
大意是x轴上方有n个目标点,坐标全为整数,为了扫描到他们,在x轴上安放雷达,每一个雷达扫描半径为d,问至少安放多少雷达。
首先想到的是化归。找出每一点在x轴上的扫描边界,即在这个范围内必须有雷达才能扫描到。这样便将问题化归为:给n个闭区间,找出最少的点,保证每个区间至少有一个点。
贪心算法:我们先将问题的区间集合按右端点排序,从最左边的区间开始,如果该区间内雷达为空为空,则选区间最右边的端点放雷达,并删掉左端点在雷达前的区间,进入子问题。
下面来证明算法的正确性:
即要证两点:
(1)贪心选择性
(2)最优子结构性质
我们先从宏观的角度来看下这个问题的结构,大家不要觉得我啰嗦啊,因为在网上看了很多题解,都只是说了个方法,但看了之后还是不知其所以然。(大牛可以ws我)
首先我们先来想想最原始的做法。就是在一堆区间中选几个点。那么我们可以拆分成若干步,每一步选一个点。选过之后,包含该点的区间都被覆盖,这样原问题划分为两个子问题,并显然满足最优子结构性质。假如雷达的坐标限定为整数,那么我们就可以采用动态规划的方法来解题。但雷达的坐标是实数,所以我们只有另辟蹊径。
经验,应该来说是通过经验,我们想到了上文所说得贪心算法。至于为什么这么做,只能说贪心算法的模式一般都是在极端处取值(可以参照活动选择问题的解法)。如果我们知道了该选择是贪心选择,即最优解的一种包含该选择,那么我们再根据上文叙述的最优子结构性质即可证明算法的正确性。(即求得的解不会比包含贪心选择的最优解大,否则与最优子问题矛盾)
下面我们证明该选择是贪心选择。
首先我们证明,子问题的第一个区间必定只能包含一个点。因为在求解子问题之前定下的点不会落在该区间,否则该区间就不会出现在子问题中;并且在求解该子问题时,该区间也不会落入两个点,否则我们去掉左边那个点,对问题没有影响(本质是因为按尾部递增排列)。请看图示:
..
----
----
----
每一个点“照看”一簇区间,左边那个点能照看到的区间,右边那个点都能照看到。因此在最优解中,第一个区间有且只有一个点。然而其他的区间则不一定,因为在子问题中新增的点可能落在之前被删掉的区间内。
这点明确之后,我们就可以开始我们的证明。因为第一个区间有且只有一点,那么假设在最优解中这一点不是放在最右端点,那么我们可以把这一点移到最右端点,并不影响它覆盖的islands。原因就不必赘述了。这样我们就得到了另一个最优解。因此,选第一个区间的最右端点是安全的选择,亦即贪心选择。
这样我们就比较完整的证明了算法的正确性了。
总结一下,深刻理解问题的结构很重要。
源代码:
#include<iostream>
#include<math.h>
using namespace std;
struct posA{
int x;
int y;
}island[1500];
struct posB{
double beg;
double end;
}interval[1500];
bool bSelect[1500];
int partition(posB *a,int p,int q)
{
int i=p;
int j=p+1;
while(j<=q)
{
if(a[j].end<a[p].end)
{
i++;
posB k=a[i];
a[i]=a[j];
a[j]=k;
}
j++;
}
posB k=a[p];
a[p]=a[i];
a[i]=k;
return i;
}
void quicksort(posB*a,int p,int q)
{
if(p<q)
{
int r=partition(a,p,q);
quicksort(a,p,r-1);
quicksort(a,r+1,q);
}
}
int findmin(int n) {
int i=0;
int sum=0;
while(1) {
double temp=interval[i].end;
int j;
bool mark=0;
for(j=0;j<n;j++)
if(bSelect[j]==0) {
if(interval[j].beg<=temp) {
bSelect[j]=true;
}
}
sum++;
for(j=0;j<n;j++)
if(bSelect[j]==0) {i=j;mark=1;break;}
if(mark==0) return sum;
}
}
int main()
{
int n,d;
double temp;
int caseindex=1;
cin>>n>>d;
while(n||d) {
bool errormark=0;
int i;
for(i=0;i<n;i++){
scanf("%d%d",&island[i].x,&island[i].y);
if(island[i].y>d) errormark=1;
}
if(d<=0||errormark==1) {cout<<"Case "<<caseindex<<": -1"<<endl;cin>>n>>d;caseindex++;continue;}
for(i=0;i<n;i++) {
temp=sqrt(double(d*d-island[i].y*island[i].y));
interval[i].beg=island[i].x-temp;
interval[i].end=island[i].x+temp;
}
quicksort(interval,0,n-1);
cout<<"Case "<<caseindex<<": "<<findmin(n)<<endl;
cin>>n>>d;
caseindex++;
for(i=0;i<n;i++)
bSelect[i]=0;
}
return 0;
}
- poj 1328 贪心算法的深入剖析
- POJ-1328(贪心算法)
- POJ 1328 贪心算法
- poj 3070 深入理解贪心的例题
- POJ 1328 简单贪心算法
- SIFT/SURF算法的深入剖析
- SIFT算法深入剖析
- 深入剖析 kmp 算法
- KeeLoq算法深入剖析
- KeeLoq算法深入剖析
- POJ 1125(用floyd算法的 + 贪心)
- POJ 1328 Radar Installation 贪心算法
- poj 1328 Radar Installation(贪心算法)
- POJ-1328 Radar Installation-贪心算法
- POJ 1328 Radar Installation - 贪心算法
- 贪心算法--雷达安装(poj 1328)
- POJ 1089 贪心算法
- poj 1017 贪心算法
- jQuery之简单计算器______初探jQuery
- Live Consciously
- ORACLE EM的删除与创建
- 快毕业了......
- 博观约取
- poj 1328 贪心算法的深入剖析
- 一次非典型性JSF调试过程
- Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤
- 测试Gmail附件链接是否长期有效
- 回味学到的观点
- 动态生成xml
- ◆解决◆双击Jbuilder2005程序一闪而过打不开问题
- C++面试经典
- 苦恼