区间覆盖问题

1. 问题描述:

Description

Farmer John is assigning some of his N (1 <= N <= 25,000) cows to do some cleaning chores around the barn. He always wants to have one cow working on cleaning things up and has divided the day into T shifts (1 <= T <= 1,000,000), the first being shift 1 and the last being shift T. 

Each cow is only available at some interval of times during the day for work on cleaning. Any cow that is selected for cleaning duty will work for the entirety of her interval. 

Your job is to help Farmer John assign some cows to shifts so that (i) every shift has at least one cow assigned to it, and (ii) as few cows as possible are involved in cleaning. If it is not possible to assign a cow to each shift, print -1.

Input

* Line 1: Two space-separated integers: N and T 

* Lines 2..N+1: Each line contains the start and end times of the interval during which a cow can work. A cow starts work at the start time and finishes after the end time.

Output

* Line 1: The minimum number of cows Farmer John needs to hire or -1 if it is not possible to assign a cow to each shift.

Sample Input

3 10
1 7
3 6
6 10

Sample Output

2

Hint

This problem has huge input data,use scanf() instead of cin to read data to avoid time limit exceed. 

INPUT DETAILS: 

There are 3 cows and 10 shifts. Cow #1 can work shifts 1..7, cow #2 can work shifts 3..6, and cow #3 can work shifts 6..10. 

OUTPUT DETAILS: 

By selecting cows #1 and #3, all shifts are covered. There is no way to cover all the shifts using fewer than 2 cows.

这道题目是POJ2376的题目

题目的大概意思是约翰这个农民有N条牛,这些牛可以在一天中的某个时间段可以进行工作,他想把一天分成若干个片段让这些牛去进行打扫任务,你的任务是安排尽量少的牛然后可以完成分成这些片段的打扫任务

 

2. 看完题目之后我们应该可以看出这个问题的本质,实际上就是区间的覆盖问题,所以我们可以在纸上画出示例中给出的数据把这些区间画出来进行分析。最少这两个字眼我们知道要使用贪心策略来解决,但是如何发现贪心的策略呢?我们很自然地想到尽量使用长的区间去覆盖给出的区间长度那么就可以使用尽量少的牛去参加清理的任务。具体来说:我们一开始的时候把区间的开始端点设置为1,循环遍历数组中的元素,把所有小于等于这个开始端点的区间都扫描一次,看一下这些区间的结束端点谁最长,我们应该在一开始的时候就定义一个最大长度,循环这些区间的时候我们都与当前的maxLen进行比较,假如当前的maxLen的长度小于当前区间的结束端点的长度那么这个时候更新maxLen,直到有的区间大于这个开始区间的端点,这个时候我们应该结束内层循环,更新下一次我们需要开始的区间的端点,然后重复上面的操作,直到所有的数组元素都扫描完了,或者找到了覆盖给出的目标区间的长度了,我们就可以退出循环了,这个时候在循环外面来判断最后的maxLen看一下与目标区间的长度来进行结果的输出

注意每一次更新下一个区间的开始端点的时候我们应该把之前的maxLen长度加1,这样提交上去的代码才是正确的

其中特别要注意开始区间的端点,区间的边界问题,因为一不注意那么提交上去的结果将会是错误的,一开始我在POJ上也是因为边界的问题,提交了很多次才Accepted

3. 具体的代码如下:

import java.util.Arrays;
import java.util.Scanner;
public class Main{
    //POJ2376提交上去可以通过
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int T = sc.nextInt(); 
        Interval intervals[] = new Interval[N];        
        for(int i = 0; i < N; i++){
            intervals[i] = new Interval(sc.nextInt(), sc.nextInt());
        }
        Arrays.sort(intervals);
        int count = 0;
        int end = 1;
        int max = 1;
        int index = 0;
        for(int i = 0; i < N; i++){
            if(i == 0 && end > 1)break;
            while(index < N && intervals[index].begin <= end){
                if(intervals[index].end > max){
                    max = intervals[index].end ;
                }
                    index++;
            }    
                    //一定为max + 1 因为要执行intervals[index].begin > end才不会出错
                    end = max + 1;
                    count++;
                    if(index < N && intervals[index].begin > end){
                        break;
                    }
                    if(end > T || index == N)break;
        }
                    //注意这里是end<=T的时候输出-1,因为赋最大值的时候例如是3 10 1 7 3 6 6 10
                    //变成了-1
                    if(end <= T){
                        System.out.println(-1);
                    }else{
                        System.out.println(count);
                    }
                    sc.close();
    }
    
    //创建内部的私有类方便输入的数据打乱后的重新排序
    private static class Interval implements Comparable<Interval>{
        int begin;
        int end;
        public Interval(int begin, int end){
            this.begin = begin;
            this.end = end;
        }
        @Override
        public int compareTo(Interval other) {
            //对于开始时间进行排序
            int x = this.begin - other.begin;
            if(x == 0){
                return this.end - other.end;
            }
            return x;
        }
    }
}

除了上面这样写,我们还可以这样写,具体的代码如下:

import java.util.Arrays;
import java.util.Scanner;
import static java.lang.Math.max;
public class Main{
  public static void main(String[] args){
    Scanner sc = new Scanner(System.in);
    int N = sc.nextInt();
    int T = sc.nextInt();
    Job[] jobs = new Job[N];
    for (int i = 0; i < N; i++) {
      jobs[i] = new Job(sc.nextInt(), sc.nextInt());
    }
    Arrays.sort(jobs);
    int start = 1;//要覆盖的目标点,end覆盖该点的所有区间中右端点最右
    int end = 1;
    int ans = 1;
    for(int i = 0; i < N; i++){
      int s = jobs[i].s;
      int t = jobs[i].t;
      if(i == 0 && s > 1)break;
      if(s <= start){//当前区间有可能覆盖start
        end = max(t, end);//更新更右的端点
      }else{//开始下一个区间
        ans++;//上一个目标覆盖已经达成,计数加1
        start = end + 1;//更新起点,设置一个新的覆盖目标
        if(s <= start){
          end = max(t, end);
        }else{
          break;
        }
      }
      if(end >= T){//当前的end超越了线段的右侧
        break;
      }
    }
    if(end < T)
      System.out.println(-1);
    else
      System.out.println(ans);
  }

  private static class Job implements Comparable<Job> {
    int s;
    int t;
    public Job(int s, int t) {
      this.s = s;
      this.t = t;
    }

    /**按照区间起点排序*/
    @Override
    public int compareTo(Job other) {
      int x = this.s - other.s;
      if (x == 0)
        return this.t - other.t;
      else
        return x;
    }
  }
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值