喷水装置(二)
时间限制:3000 ms | 内存限制:65535 KB
难度:4
- 描述
- 有一块草坪,横向长w,纵向长为h,在它的橫向中心线上不同位置处装有n(n<=10000)个点状的喷水装置,每个喷水装置i喷水的效果是让以它为中心半径为Ri的圆都被润湿。请在给出的喷水装置中选择尽量少的喷水装置,把整个草坪全部润湿。
- 输入
- 第一行输入一个正整数N表示共有n次测试数据。
每一组测试数据的第一行有三个整数n,w,h,n表示共有n个喷水装置,w表示草坪的横向长度,h表示草坪的纵向长度。
随后的n行,都有两个整数xi和ri,xi表示第i个喷水装置的的横坐标(最左边为0),ri表示该喷水装置能覆盖的圆的半径。 输出 - 每组测试数据输出一个正整数,表示共需要多少个喷水装置,每个输出单独占一行。
如果不存在一种能够把整个草坪湿润的方案,请输出0。 样例输入 -
2 2 8 6 1 1 4 5 2 10 6 4 5 6 5
样例输出 -
1
2
-
-
分析题目:
-
①本题若是直接用题目中喷泉的两个参数求解的话,根本无从下手,所以先得分析题目,将参数其转化为区间两个端点,就把题目转化为了区间覆盖的问题
-
②贪心的策略也很明显:先将喷泉排序,优先按起始坐标从小到大排序,起始坐标一样的再按结束坐标从大到小排序 。然后每次选择时,都从那些满足起始坐标小于上一个喷泉的结束坐标的喷泉中选,选择它们中结束坐标最大的那个(贪心)。
-
代码如下(有注释)
-
#include<iostream> #include<cmath> #include<fstream> #include<cstdio> #include<algorithm> using namespace std; struct D { double st; //起始坐标 double ed; //结束坐标 }d[10005]; bool comp(const D & d1,const D & d2)//优先按起始坐标从小到大排序,起始坐标一样的再按结束坐标从大到小排序 { if(d1.st==d2.st) return d1.ed>d2.ed; return d1.st<d2.st; } int main() { //freopen("in.txt","r",stdin); int times;//样例的次数 scanf("%d",×); while(times--) { int i=0; int number,w,h;//分别为喷泉的个数,花园的宽和高 scanf("%d%d%d",&number,&w,&h); double a=h*1.0/2; while(number--) { int st,r; scanf("%d%d",&st,&r); if(r<=(h*1.0/2)) continue; double lh=2*sqrt(r*r-a*a); d[i].ed=st+lh/2; d[i++].st=st-lh/2; } //while里面是将喷泉的两个参数转化为起始坐标和结束坐标并且存入结构体数组中去 sort(d,d+i,comp); //排序 int ans;//最终答案 int j=0;//用来遍历结构体数组 int over=0;//一个标志量,若over赋值为一,就直接输出0,表示没有合适方案 if(d[j].st>0) //如果第一个喷泉的起始坐标大于0,那么花园最左边的那肯定喷洒不到 { printf("0\n"); break; } else { ans=1;//否则的话,必定要选第一个喷泉,那么ans赋值为1 if(d[j].ed<w)//判断第一个喷泉的结束坐标是否已经大于了花园的宽度 { for(j=0;j<i-1&&d[j].ed<w;)//每次判断一下喷泉是否已经用光了或者已经达到题目要求了 { double maxed=0;//用来记录满足条件的喷泉中结束坐标的最大值 int flag=0;// 用来记录满足条件中的喷泉结束坐标的最大值的下标 for(int t=j+1;t<i&&d[t].st<=d[j].ed;t++) { if(d[t].ed>d[j].ed&&d[t].ed>maxed) { maxed=d[t].ed; flag=t; } } if(!maxed) //如果maxed仍然为0,那么说明没有找到任何满足条件的喷泉,直接将over赋值为1 { over=1; printf("0\n"); break; } j=flag;//选择满足条件的喷泉中结束坐标最大的那个喷泉作为新一轮的开始 ans++;//选中的喷泉个数自增一次 } } } if(!over)//每次都找到了任何满足条件的喷泉 printf("%d\n",ans); } return 0; }
- 第一行输入一个正整数N表示共有n次测试数据。