三类基于贪心思想的区间覆盖问题

一、区间完全覆盖问题

问题描述:给定一个长度为m的区间,再给出n条线段的起点和终点(注意这里是闭区间),求最少使用多少条线段可以将整个区间完全覆盖。

样例:一个长度为8的区间,可选的线段有[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]。

求解过程:

1、将每一条线段按左端点递增顺序排列,如果左端点相同,按右端点递增顺序排列,排完序后为[1,4],[2,4],[2,6],[3,5],[3,6],[3,7],[6,8];

2、设置一个变量表示已覆盖到的区间右端点,在剩下的线段中找出所有左端点小于等于当前已覆盖到的区间右端点的线段,选择右端点最大并且大于当前已覆盖到的区间右端点,重复以上操作直至覆盖整个区间;

3、模拟过程:假设第一次加入[1,4],那么下一次能够选择的线段有[2,6],[3,5],[3,6],[3,7],由于3小于4且7最大,所以下一次选择[3,7]进行覆盖,最后一次只能选择[6,8],这个时候刚好覆盖长为8的区间-->break;即所选3条线段就能覆盖长度为8的大区间;

4、贪心证明:

要求用最少的线段进行覆盖,那么选取的线段必然要尽量长,而已覆盖到的区域之前的地方已经不用考虑了,可以理解成所有可覆盖的左端点都已被覆盖了,那么能够使得线段更长的取决于右端点,左端点没有太大的意义,所以选择右端点来覆盖。

题解报告:NYOJ #12 喷水装置(二)

描述

有一块草坪,横向长w,纵向长为h,在它的橫向中心线上不同位置处装有n(n<=10000)个点状的喷水装置,每个喷水装置i喷水的效果是让以它为中心半径为Ri的圆都被润湿。请在给出的喷水装置中选择尽量少的喷水装置,把整个草坪全部润湿。

输入

第一行输入一个正整数N表示共有n次测试数据。
每一组测试数据的第一行有三个整数n,w,h,n表示共有n个喷水装置,w表示草坪的横向长度,h表示草坪的纵向长度。
随后的n行,都有两个整数xi和ri,xi表示第i个喷水装置的的横坐标(最左边为0),ri表示该喷水装置能覆盖的圆的半径。

输出

每组测试数据输出一个正整数,表示共需要多少个喷水装置,每个输出单独占一行。
如果不存在一种能够把整个草坪湿润的方案,请输出0。

扫描二维码关注公众号,回复: 3657428 查看本文章

样例输入

2
2 8 6
1 1
4 5
2 10 6
4 5
6 5

样例输出

1
2
解题思路:典型的区间完全覆盖问题。由于喷水装置是安置在横向中心线上并且圆具有对称性,故只需取高度的一半,然后将每个喷水装置能够覆盖的区间范围映射成在x轴的长度,然后按上面的方法贪心选线段即可。
AC代码:
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=10005;
 4 int t,n,k,cnt,pos,beg;double w,h,lb,xi,ri,maxv;pair<double,double> itv[maxn];bool flag;
 5 int main(){
 6     while(cin>>t){
 7         while(t--){
 8             cin>>n>>w>>h;h/=2.0;pos=cnt=0;//h取一半
 9             for(int i=0;i<n;++i){
10                 cin>>xi>>ri;
11                 if(ri<h)continue;//ri==h也要算
12                 itv[pos].first=xi-sqrt(ri*ri-h*h);
13                 itv[pos++].second=xi+sqrt(ri*ri-h*h);
14             }
15             sort(itv,itv+pos);lb=0;beg=0;flag=false;
16             if(itv[0].first>0){cout<<0<<endl;continue;}//按左端点排序只需查看最左边的端点是否满足条件即可,最右边的端点在下面有判断
17             while(lb<w){
18                 maxv=0;
19                 for(k=beg;k<pos&&itv[k].first<=lb;++k)//itv[k].first<=lb这样保证整个区间是连续的,即草坪都会被润湿
20                     maxv=max(maxv,itv[k].second);//找线段左端点在lb以内右端点能覆盖到的最远距离
21                 if(maxv>lb)cnt++,lb=maxv,beg=k;//如果有一条线段右端点比当前已覆盖的区间右端点lb还大,那么就更新已覆盖的右端点值,同时计数器加1
22                 else {flag=true;break;}//否则说明不能覆盖整个区间,直接退出,输出0
23             }
24             if(flag)cout<<0<<endl;
25             else cout<<cnt<<endl;
26         }
27     }
28     return 0;
29 }

猜你喜欢

转载自www.cnblogs.com/acgoto/p/9824723.html
今日推荐