稻草人的祝福(构建贪心模型)

题意:每个庄稼都可以看做是坐标系里面的一个点,当它处于某个稻草人的范围内时就可以视为被保护.每个稻草人的辐射范围都是一个半径为R的圆.稻草人只在坐标系的x轴上,而任何庄稼(x,y)都满足y>0。设计一个方案,用尽可能少的稻草人来保证所有庄稼都是安全的.若存在无法覆盖的庄稼或者给的稻草人不够覆盖所有庄稼,输出-1.

分析:本题是很经典的一道构建贪心模型的题.

我们以庄稼为圆心,仍以R为半径作圆,该圆与x轴有两个交点(前提是该庄稼的纵坐标小于圆的半径),则能够保护它的稻草人一定处于这两个点之间.是不是觉得这个转化很神奇!所以原题就转化成了给定n个区间,然后要给一些点染色,使得每个区间中都至少有一个点被染色,使染色数最少.

区间染色问题,就是贪心的模板题了.以左端点排序或者以右端点排序都可以.我的代码中是以左端点排序,简述一下以左端点排序的思路:我们枚举这n个区间,记录右边界,如果当前枚举的区间的右端点比右边界小,右边界取较小值更新为该区间的右端点(这里就是贪心策略,画几个区间就明白了);如果当前枚举的区间的左端点比右边界大,ans++,右边界记得更新为当前区间的右端点.

int n,R,c;
int x[50005],y[50005];
struct farm{
    double l,r;
}a[50005];//结构体记录区间的左右端点,便于排序.
void get_lr(int id,int x,int y){
    double xx=sqrt(R*R-y*y);
    a[id].l=x*1.0-xx;
    a[id].r=x*1.0+xx;
}//将庄稼的坐标转化为区间的左右端点.
bool cmp(farm a,farm b){
    if(a.l==b.l)return a.r<b.r;
    return a.l<b.l;
}//按照左端点从大到小排序.
int main(){
    while(1){//多组数据
		n=read();R=read();c=read();
//n个庄稼,R是稻草人能保护的圆的半径,c的稻草人的数量
		if(!n&&!R&&!c)return 0;
		int bj=0;
		for(int i=1;i<=n;i++){
	    	x[i]=read();y[i]=read();
	    	get_lr(i,x[i],y[i]);
	    	if(y[i]>R)bj=1;
		}
//如果有一个庄稼的纵坐标大于半径,肯定保护不了
		if(bj){puts("-1");continue;}
		sort(a+1,a+n+1,cmp);
		int now=1,ans=1;
		for(int i=2;i<=n;i++){
	    	if(a[i].l>a[now].r){
				ans++;now=i;
	    	}
	    	if(a[i].r<a[now].r)now=i;
		}
		if(ans<=c)printf("%d
",ans);
		else puts("-1");
    }
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/10338524.html