ACM_12喷水装置(二)

来源:互联网 发布:少年维特之烦恼知乎 编辑:程序博客网 时间:2024/05/22 09:43

喷水装置(二)

时间限制:3000 ms  内存限制:65535KB
难度: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。
样例输入
22 8 61 14 52 10 64 56 5
样例输出
12
来源
《算法艺术与信息学竞赛》
上传者
张云聪


import java.util.ArrayList;
import java.util.Scanner;

public class Greedy{

    publicstatic void main(String[] args) {
         Greedy greedy = new Greedy();
         greedy.solution();
    }
   
    public voidtest(){
       ArrayListLines = new ArrayList();
      Lines.add(new Line(1,4));
      Lines.add(new Line(2,3));
      Lines.add(new Line(3,6));
      Lines.add(new Line(2,5));
       Lines =choose(Lines);
      
      System.out.println(Lines);
    }
   
    public voidsolution() {
       Scanner in =new Scanner(System.in);
       int groups =in.nextInt();
       for (int i =0; i < groups; i++) {
          int size = in.nextInt();
          if(size == 0){
             System.out.println(0);
             continue;
          }
          int length = in.nextInt();
          int width = in.nextInt();
          int[][] points = new int[size][2];
          
          for(int j = 0 ; j < size ; j++){
             points[j][0] =in.nextInt();
             points[j][1] =in.nextInt();
          }
          ArrayList Lines = change(width,points);
          
          if(Lines.size() == 0){
             System.out.println(0);
             continue;
          }
          
          Lines = choose(Lines);
          
          int MinNum = judge(length, Lines);
          System.out.println(MinNum);
       }
    }
   
   //将横坐标+洒水半径的点集转化成能覆盖到的最小横坐标和最大横坐标的点集,这一步中将筛选掉不能纵向覆盖草坪的喷水器
   
    privateArrayList change(int width,int[][] points){
       doublehalfwidth = width/2.0;
      ArrayList  Lines = new ArrayList();
       for(int i =0 ; i < points.length ;i++){
         if(points[i][1] < halfwidth){
            continue;
          }
         
          double r =Math.sqrt(points[i][1]*points[i][1] - halfwidth*halfwidth);
         
         Lines.add(new Line(points[i][0]-r,points[i][0]+r));
       }
       returnLines;
    }
   
   //筛选掉能相互重叠的片段 E.G  (1,4)  能覆盖掉(2.3) 那么则去除(2,3)
   
    privateArrayList choose(ArrayList Lines){
       for(int i =0 ; i < Lines.size() ; i++){
          for(int j =i+1; j < Lines.size() ; j++){
            if(Lines.get(i).start >= Lines.get(j).start &&Lines.get(i).end <= Lines.get(j).end){
               Lines.remove(i);
                i--;
                break;
             }
            
            if(Lines.get(i).start <= Lines.get(j).start &&Lines.get(i).end >=Lines.get(j).end){
               Lines.remove(j);
                j--;
               continue;
             }
          }
       }
       returnLines;
    }
   
   //判断需要最少需要的多少段才能覆盖整个线段    采用算法 贪婪    每一步选择都选择与先前重叠部分最小的块
    private intjudge(int length , ArrayList Lines){
       int mark =-1;
       double end =-1; // 记录已经覆盖的长度;
      
       int count =0;
      //选择第一段
       for(int i =0 ; i  < Lines.size() ; i++){
         if(Lines.get(i).start <=0){
            if(Lines.get(i).end >= end){
                end =Lines.get(i).end;
                if(mark>= 0){
                  Lines.remove(mark);
                   i--;
                }
                mark =i;
             }else{
               Lines.remove(i);
                i--;
             }
          }
       }
      
       if(end <=0){
          return0;
       }
      
      count++;
       
       double gap =-1;
       while(end< length){
          mark =-1;
          gap =-1;
          for(int i =0 ; i  < Lines.size() ; i++){
             if(Lines.get(i).start <= end){
                if(Lines.get(i).end - end >= gap){
                    gap =Lines.get(i).end - end;
                    if(mark>= 0){
                      Lines.remove(mark);
                       i--;
                    }
                    mark =i;
                 }else{
                   Lines.remove(i);
                    i--;
                 }
              }
           }
         
          if(mark>= 0){
             end =Lines.get(mark).end;
            Lines.remove(mark);
            count++;
          }else{
             return0;
          }
       }
       returncount;
    }
   
    //保存线段的起点的终点
   
    classLine{
       doublestart=0;
       double end =0;
      
       publicLine(double start,double end){
          this.start =start;
          this.end =end;
       }
      
       publicString toString(){
          returnstart+" "+ end;
       }
    }
}



比较简单的贪婪算法,主要简单在于 不要求重复覆盖最小,  我是利用这个构思的

1将能被其他单一喷雾器完全取代的个体全部去除

长度0位开始覆盖,然后依次选取能与已覆盖连接的能增长最多覆盖范围的喷水器。



这样保证是正确解的理由
这样得到的结果 块数要小于等于最优解     最优解的每一块 可以映射一个覆盖面积大于等于他的块
同时块数要大于等于最优解
那么结果就是最优解
0 0
原创粉丝点击