地址:http://acm.nyist.net/JudgeOnline/problem.php?pid=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。
样例输入
-
2 2 8 6 1 1 4 5 2 10 6 4 5 6 5
样例输出
-
1 2
-
第一行输入一个正整数N表示共有n次测试数据。
思路:
这道题,测试用例都是费的,我之前的做法就一直是wrong answer.测试用例过了,不代表能过。最主要是把问题分析好。很明显就是排序,然后不断的试,看是否符合条件
(贪心)。。但是关键就是在这个条件的判断。
从这里http://blog.youkuaiyun.com/pointerking/article/details/22686001 了解到了该怎么解决,虽然耗尽心思,觉得懂了,但不担保,同类型的会动。只是觉得很神奇,很佩服想
到是怎么做的。不买关子先,直接解释下。。
首先对读入的数据要做一次判断:
if(NodeArrArr[i].r<=h/2){ //不能构成一个完整的三角形,斜边要大于直角边
i--;
n--;
continue;
}
也就是半径要大于直角边:
只有构成了这种情况,才能临界覆盖整个长方向。
接着:
NodeArrArr[i].left=NodeArrArr[i].x-Math.sqrt(NodeArrArr[i].r*NodeArrArr[i].r-h*h/4);
将情况分为left和right。
left有三种情况
==0,是临界,<0肯定是可以的,>0肯定不满足
而右边的情况:NodeArrArr[i].right=NodeArrArr[i].x+Math.sqrt(NodeArrArr[i].r*NodeArrArr[i].r-h*h/4);
只有大于长度w的情况,和<w的情况。
感觉自己还是讲得不够清楚,可以讲案例的数据带进去,模拟下,画下图就可以理解了。进行实践才是最好的学习方式。
import java.util.Scanner;
/*
* 170 662
* 喷水装置二
*/
public class Main1{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int m=sc.nextInt();
while(m-->0){
int n=sc.nextInt();
double w=sc.nextDouble();
double h=sc.nextDouble();
NodeArr[] NodeArrArr=new NodeArr[n];
for(int i=0;i<n;i++){
NodeArrArr[i]=new NodeArr();
NodeArrArr[i].x=sc.nextDouble();
NodeArrArr[i].r=sc.nextDouble();
if(NodeArrArr[i].r<=h/2){ //不能构成一个完整的三角形,斜边要大于直角边
i--;
n--;
continue;
}
//NodeArrArr[i].r*NodeArrArr[i].r-h*h/4--得到刚好能覆盖长方形的情况,看是否给出的数据符合
NodeArrArr[i].left=NodeArrArr[i].x-Math.sqrt(NodeArrArr[i].r*NodeArrArr[i].r-h*h/4);
NodeArrArr[i].right=NodeArrArr[i].x+Math.sqrt(NodeArrArr[i].r*NodeArrArr[i].r-h*h/4);
}
NodeArrArr=insertSort(NodeArrArr,n); //直接插入排序,从大到小
int count=0;
int flag=0;
double max=0,begin=0;
for(int i=0;i<n;i++){
while(i<n&&NodeArrArr[i].left<=begin){ //由最大的开始,负数,就一定可以覆盖,就左边而言
if(NodeArrArr[i].right>max) //在上面的情况下,找到最大的--right=x+need,下面的与w判断
max=NodeArrArr[i].right;
i++;
}
i--; //不怕越界,后面有判断出去的。
//每次都要判断一下Max的值,看是否已经出界,若出界则跳出,否则继续贪心
if(max>=w){
count++;
begin=max;
break;
}
else if(begin<max){ //Max不出界,则正常向右贪心
count++;
begin=max;
}
else{ //两点以上的中间会有断点
flag=1;
break;
}
}
if(flag==1||begin<w){ //右边盖不住
System.out.println("0");
}
else{
System.out.println(count);
}
}
}
public static NodeArr[] insertSort(NodeArr[] arr,int length){
double key1,key2;
for(int i=1;i<length;i++){
int j=i-1;
key1=arr[j].left;
key2=arr[j].right;
while(j>0&&key1<arr[j-1].left){
arr[j].left=arr[j-1].left;
arr[j].right=arr[j-1].right;
j--;
}
arr[j].left=key1;
arr[j].right=key2;
}
return arr;
}
}
class NodeArr{
double x,r,left,right;
}
最后补充下知识点--区间的贪心:http://shu-mj.com/archives/167