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;
}

原创粉丝点击