版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/violinlove/article/details/82814874
求区间与区间的最大不覆盖数—套板子(区间右端点从小到大排序,选择不冲突的,尽可能的选)简记为线段覆盖问题
求区间内最少单点满足覆盖要求,不管是各个区间需要一个点还是一个子区间(这个子区间是可连续、可不连续的)
按照区间右端点从小到大排序,依次满足各个区间的要求,如此下来种的树(需要的点)最少。
如果想不出贪心,线段树+优化亦可作。
选择尽量少的区间覆盖指定线段区间。
【 模板 】将所有的区间按照左端点从小到大排序,对于当前查找的起点s ,选择覆盖点 s 的区间中右端点中最大的一个,并将 s 更新为此区间的右端点,(这样区间数目+1)直到s>=整个线段长度。(在s时刻,找一个左端点小于等于s的最大右端点)
对于这道题,需要进行一些转化——每个浇灌喷头受限于所能喷到的最短距离,它的最短距离是半径到达草坪上端。此时,如果将整个草坪长度想象为 x轴的正半轴,那么,我们应该根据勾股定理找到水平距离,即()。把当前点的pos减去水平距离作为左端点,pos+水平距离作为右端点。然后就是套上面的板子↑。
PS:一定要理解了过程再写,一定要理解了过程再写,一定要理解了过程再写!!!
附上AC代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define N 300005
using namespace std;
const double Eps = 1e-5 ;
inline int wread(){
char c=getchar ();int flag=1,wans=0;
while (c<'0'||c>'9'){if (c=='-') flag=-1;c=getchar ();}
while (c>='0'&&c<='9'){wans=wans*10+c-'0';c=getchar ();}
return wans*=flag;
}
int T;
int n,L,W;
struct node {double lx,rx;int pos,r;}a[N];
bool e666 (node x,node y){return x.lx<y.lx;}
int main (){
T=wread();
while (T--){
memset (a,0,sizeof a);
n=wread();L=wread();W=wread();
for (int i=1;i<=n;++i){
a[i].pos=wread(),a[i].r=wread();
if (a[i].r*a[i].r-(W*1.0/2.0)*(W*1.0/2.0)<0) a[i].pos=-1,a[i].lx=-1,a[i].rx=-1;
//对于不能覆盖完草坪上端的一个圆(例如样例1中的那个夹在草坪中间的圆) 肯定是不会选它的
else {
a[i].lx=max (0.000000,(double)a[i].pos-sqrt((double)a[i].r*(double)a[i].r-(W*1.0/2.0)*(W*1.0/2.0)));
a[i].rx=a[i].pos+sqrt((double)a[i].r*(double)a[i].r-(W*1.0/2.0)*(W*1.0/2.0));
}
}
sort (a+1,a+n+1,e666);
int nx=1;
double s=0;//起点
bool F=true;//判断是否能覆盖完整个区间
int ans=0;//记录
while (nx<=n){
ans++;
double re=s;
while (a[nx].lx<=re && nx<=n){//模板
if (a[nx].rx>=re) s=max(s,a[nx].rx);
nx++;
}
if (s>=L) {break;}
if (re==s) {F=false;break;}//未更新,请画图____即当前区间与下个区间之间 一定有一段距离,不符合全部覆盖的要求,退出
}
if (!F) puts("-1");
else printf("%d\n",ans);
}
return 0;
}
未完待续...