白书中的 区间贪心例题。POJ 2376,1328,3190(各种区间分布的最大最小问题,优先队列)

POJ 2376   Cleaning Shifts

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 51535   Accepted: 12380

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.     本题的题意是给你一个最大时间范围。以及几个时间区间。求至少需要几个区间才能将时间范围覆盖。 因此我们贪心的选取由该起点即之前为起点的右边界最大值即可。 AC代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#include<queue>
#include<string>
#include<map>
#include<sstream>
#define MAXN 1000005
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int r[MAXN];//r[]代表以这个时间为起点时的最大终点 
int main(){
    int n,t;
    scanf("%d%d",&n,&t);
    for(int i=0;i<n;i++){
        int temp1,temp2;
        scanf("%d%d",&temp1,&temp2);
        if(temp2>r[temp1])//我们只存找到最大的即可 
            r[temp1]=temp2;
    }
    int ans=0,tf=0,ttemp=0;//tf表示最右边界,ttemp表示目前找到的最右边界 
    for(int i=1;i<=t;i++){
        if(ttemp<r[i]){//如果在这里找到更右的了,那么先更新到ttemp 
            ttemp=r[i];
        }
        if(tf<i){//如果当前的时间已经过了前面的右边界,那么就需要更新了 
            if(ttemp>=i){//如果前面区间内找到的最右值大于当前时间,那么赋值 
                ++ans;//多加入一个时间区间 
                tf=ttemp;
            }else{//如果前面所有的区间都不能覆盖到这个时间 
                printf("-1\n");
                return 0;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

POJ1328  Radar Installation

 

Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 145339   Accepted: 32012

 

Description

 

Assume the coasting is an infinite straight line. Land is in one side of coasting, sea in the other. Each small island is a point locating in the sea side. And any radar installation, locating on the coasting, can only cover d distance, so an island in the sea can be covered by a radius installation, if the distance between them is at most d.

We use Cartesian coordinate system, defining the coasting is the x-axis. The sea side is above x-axis, and the land side below. Given the position of each island in the sea, and given the distance of the coverage of the radar installation, your task is to write a program to find the minimal number of radar installations to cover all the islands. Note that the position of an island is represented by its x-y coordinates.
白书中的  区间贪心例题。POJ 2376,1328,3190(各种区间分布的最大最小问题,优先队列)
Figure A Sample Input of Radar Installations


 

Input

 

The input consists of several test cases. The first line of each case contains two integers n (1<=n<=1000) and d, where n is the number of islands in the sea and d is the distance of coverage of the radar installation. This is followed by n lines each containing two integers representing the coordinate of the position of each island. Then a blank line follows to separate the cases.

The input is terminated by a line containing pair of zeros

 

Output

 

For each test case output one line consisting of the test case number followed by the minimal number of radar installations needed. "-1" installation means no solution for that case.

 

Sample Input

 

3 2
1 2
-3 1
2 1

1 2
0 2

0 0

 

Sample Output

 

Case 1: 2
Case 2: 1

本题的题意是先计算出该点能由x轴上哪个区间获得。之后遍历区间,找到所有区间的最少的并集。即找到最少的点使所有的区间都在内。
思路是按优先起点其次终点对所有区间进行升序排序。依次遍历每个区间。根据他们的边界关系不断更新答案区间。
如果答案区间完全包含一个区间,那么就令答案区间等于该区间。
如果一个区间的一半在答案区间,由于我们按起点排序。那么那个区间的起点肯定在答案区间内,终点在外。故令答案区间的起点为该区间的起点。
如果一个区间完全在答案区间外,那么我们需要新建一个答案区间。并令计数++。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#include<queue>
#include<string>
#include<map>
#include<sstream>
#define MAXN 1000005
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int n;
int d;
struct node{
    double l,r;
}a[1005];
int cmp(const node &a,const node &b){
    if(a.l==b.l)
        return a.r<b.r;
    return a.l<b.l; 
}
int main(){
    int id=0;
    while(scanf("%d%d",&n,&d),n){
        int flag=1;
        for(int i=0;i<n;++i){
            double x,y;
            scanf("%lf%lf",&x,&y);
            if(y>d){
                flag=0;
            }else{
                double temp=d*d-y*y;
                a[i].l=x-sqrt(d*d-y*y);
                a[i].r=x+sqrt(d*d-y*y);//确认该点所需的区间 
            }
        }
        if(!flag){//如果有点比半径还大,那么就不行 
            printf("Case %d: -1\n",++id);
            continue;
        }
        sort(a,a+n,cmp);//按起点进行排序 
        int cnt=1;
        double templ=a[0].l,tempr=a[0].r;
        for(int i=1;i<n;i++){
            if(a[i].l>=templ&&a[i].r<=tempr){//如果答案区间完全包含一个区间,那么就令答案区间等于该区间。
                templ=a[i].l;
                tempr=a[i].r;
            }else if(a[i].l<=tempr&&a[i].r>=tempr)//如果一个区间的一半在答案区间,令答案区间的起点为该区间的起点。 
                templ=a[i].l;
            else if(a[i].l>tempr){//如果完全在外,就新建答案区间 
                templ=a[i].l;
                tempr=a[i].r;
                ++cnt;
            }
        }
        printf("Case %d: %d\n",++id,cnt);
    }
    return 0;
}

POJ 3190  Stall Reservations

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 19027   Accepted: 6569   Special Judge

Description

Oh those picky N (1 <= N <= 50,000) cows! They are so picky that each one will only be milked over some precise time interval A..B (1 <= A <= B <= 1,000,000), which includes both times A and B. Obviously, FJ must create a reservation system to determine which stall each cow can be assigned for her milking time. Of course, no cow will share such a private moment with other cows.

Help FJ by determining:
  • The minimum number of stalls required in the barn so that each cow can have her private milking period
  • An assignment of cows to these stalls over time
Many answers are correct for each test dataset; a program will grade your answer.

Input

Line 1: A single integer, N

Lines 2..N+1: Line i+1 describes cow i's milking interval with two space-separated integers.

Output

Line 1: The minimum number of stalls the barn must have.

Lines 2..N+1: Line i+1 describes the stall to which cow i will be assigned for her milking period.

Sample Input

5
1 10
2 4
3 6
5 8
4 7

Sample Output

4
1
2
3
2
4

Hint

Explanation of the sample:

Here's a graphical schedule for this output:

Time     1  2  3  4  5  6  7  8  9 10

Stall 1 c1>>>>>>>>>>>>>>>>>>>>>>>>>>>
Stall 2 .. c2>>>>>> c4>>>>>>>>> .. ..
Stall 3 .. .. c3>>>>>>>>> .. .. .. ..
Stall 4 .. .. .. c5>>>>>>>>> .. .. ..
Other outputs using the same number of stalls are possible.
 

本题的题意是是给你几个时间区间和一个时间范围。将时间区间不重复的放入时间范围。需要多少层。(优先队列)

本题和CF1498B Box Fitting的感觉差不多。应该也能做。(不过本题更麻烦一点)。

本题纯贪心的话会TLE。不过我在洛谷上用类如register的时间优化后卡过了评测。(但是在poj上还是tle了呜呜)

本来的思路是进行对每个区间排序后,遍历一遍数据找到一个stall。然后继续遍历知道每个点都有归属。

那么我们发现这最坏是n2的时间复杂度也就是说是有概率过不了的(poj的数据还是挺狠的。。)

故我们用优先队列来同时处理多个stall。

因此我们将stall存入优先队列,将区间的结束时间作为排序依据。并将时间区间排序。

接下来遍历时间区间。

如果优先队列为空,那么就新建一个stall并将第一个时间区间的结束时间入列。

如果优先队列不为空,那么看他的结尾时间。

由于区间排过序,如果该区间的开始时间也小于这个结尾时间,那么就没有stall的结尾区间小于该区间的开始时间,即没有stall可以容下这个区间了,只能新建一个stall。

如果该区间的开始时间大于这个结尾时间,那么就贪心的将他放入这个stall,同时更新结尾时间。(即把原结尾时间出列,新结尾时间入列。这个stall新的结尾时间)。

优先队列的好处就在于队首元素所代表的是最容易进入的stall。如果这个都进不了,那么就没有能进的只能新建stall了。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#include<queue>
#include<string>
#include<map>
#include<sstream>
#define MAXN 50005
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int n;
int d;
int vis[MAXN];
struct node{
    int id,l,r;
    friend bool operator< (node a,node b){//重载运算符用于sort 
        return a.l<b.l;
    }
}a[MAXN];
struct stall{
    int id,r;
    bool friend operator< (stall a,stall b){//重载运算符用于priority_queue。由于默认最大堆,故运算符需要反一下 
        return a.r>b.r;
    }
    stall(int a,int b):r(a),id(b){}//构造函数 
};
priority_queue<stall>q;
int main(){
    int n;
    scanf("%d",&n);
    for(register int i=0;i<n;i++){
        scanf("%d%d",&a[i].l,&a[i].r);
        a[i].id=i;//记录id,防止被sort后扰乱 
    }
    sort(a,a+n);//排序 
    int r,cnt=0;
    for(register int i=0;i<n;++i){
        if(q.empty()){//如果队内没有元素。 
            vis[a[i].id] = ++cnt;//新建一个stall 
            q.push(stall(a[i].r,cnt));//入列 
        }else{
            stall u=q.top();//队首元素,即最容易加入的stall 
            if(u.r<a[i].l){//如果下一个区间可以加入这个stall就加入 
                q.pop();
                vis[a[i].id]=u.id;
                q.push(stall(a[i].r,u.id));
            }else{//如果下一个区间不能加入这个stall 
                vis[a[i].id]=++cnt;//只能新建一个stall了 
                q.push(stall(a[i].r,cnt));
            }
        }
    }
    printf("%d\n",cnt);
    for(int i=0;i<n;i++){
        printf("%d\n",vis[i]);
    }
    return 0;
}

 

 

上一篇:SQL5


下一篇:C++[USACO06FEB]Backward Digit Sums