AcWing 111 畜栏预定

题目描述:

有N头牛在畜栏中吃草。每个畜栏在同一时间段只能提供给一头牛吃草,所以可能会需要多个畜栏。给定N头牛和每头牛开始吃草的时间A以及结束吃草的时间B,每头牛在[A,B]这一时间段内都会一直吃草。当两头牛的吃草区间存在交集时(包括端点),这两头牛不能被安排在同一个畜栏吃草。求需要的最小畜栏数目和每头牛对应的畜栏方案。

输入格式

第1行:输入一个整数N。第2..N+1行:第i+1行输入第i头牛的开始吃草时间A以及结束吃草时间B,数之间用空格隔开。

输出格式

第1行:输入一个整数,代表所需最小畜栏数。第2..N+1行:第i+1行输入第i头牛被安排到的畜栏编号,编号从1开始,只要方案合法即可。

数据范围

1≤N≤50000,1≤A,B≤1000000

输入样例:

5
1 10
2 4
3 6
5 8
4 7

输出样例:

4
1
2
3
2
4

分析:

本题是经典的多重区间贪心问题(自己起的名字)。为什么这么这么称呼这题呢,先引入下经典的区间贪心问题。有n项工作。每项工作分别在si时刻开始,ti时刻结束,某人可以随机选择参加某项工作,但是一旦参加必须全程参与直至结束,求某人最多能参加多少项工作。这道题的解法就是将这些工作的区间按照结束时间排序,每次优先参加结束时间早的工作,这样的策略会让他完成的工作项数最多。

本题本质上等价于这样一个问题:有n项工作。每项工作分别在si时刻开始,ti时刻结束,某人可以随机选择参加某项工作,但是一旦参加必须全程参与直至结束,求完成所有的工作至少要多少人参与。

稍加思考可以发现本题和转化后的问题是完全等价的。那么是否可以多次调用之前经典的区间问题的算法去求解本题呢?也就是先让第一个人尽可能多的按照上面的策略完成工作,并标记已经完成的工作,第二个人按照同样的策略去完成还没有完成的工作,直至所有工作都被完成为止。一开始我就是采取这种思路的,但是数据集一多,便WA了,逻辑上出现了错误。

贪心问题有几个难点:其一是策略不易想到,想到了也不易证明;其二是每个人的算法都是选取某种较优的策略去进行,但是并不知道是不是最优的,以至于在数据量不够的情况下,可能结果都是对的,在评测系统上更为致命,通过了所有样例,其实算法还是错误的,一提交就没分了,根本无法判断对错。可以发现,上面的思路其实相当于给了每个人不同的选择权限,第一个人选完工作,第二个人才能开始选。这样会导致,即使某人有空闲,恰好之后有工作开始,他也不能去工作,而是等优先级高的人做完手头的工作再去选。

正确的思路也就是我们正常的思路:没有平行时空,也没有优先级,时间向后流逝,某工作开始了,如果已经雇佣的人当中有人空闲就安排他去做,否则就要雇新人,毕竟工作不等人,错过了时机就失去了完成的机会。所以我们需要首先对工作开始时间进行排序,再维护一个大根堆,将每项工作的结束时间的相反数存进去(这样相当于堆顶是最小的结束时间)。一旦有新工作开始,首先看看堆里有没有人空闲,也就是查看堆顶元素(最先做完工作的那个人结束之前工作的时间是否在新工作开始之前)。如果有人空闲,那么就安排他做,同时将堆里原来这个人工作的结束时间替换为新工作的结束时间。如果没有,那么就新安排人手去完成。

综上所述,之所以称呼本题为多重区间贪心问题,就是因为还是按照工作结束时间排序,只不过有多个人可以用,最先完成工作的人,可以最先选择还未进行的工作去完成,而不用等待最先开始工作的人去完成。

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 50005;
int ans[maxn];
struct cows{
    int st,end,num,arr;
    bool operator < (const cows x) const{//按开始时间排序
        return st < x.st;
    }
}c[maxn];
priority_queue<pair<int,int> > s;//<结束时间的相反数,被安排的畜栏的编号>
int main(){
    int n;
    cin>>n;
    for(int i = 0;i < n;i++){
        c[i].num = i;
        cin>>c[i].st>>c[i].end;
    }
    sort(c,c + n);
    for(int i = 0;i < n;i++){
        int size = s.size();
        if(size && -s.top().first < c[i].st){//有畜栏空闲则选择空闲最久的那个
            c[i].arr =s.top().second;
            s.pop();
            s.push({-c[i].end,c[i].arr});
            continue;
        }
        c[i].arr = ++size;
        s.push({-c[i].end,size});
    }
    cout<<s.size()<<endl;
    for(int i = 0;i < n;i++)    ans[c[i].num] = c[i].arr;
    for(int i = 0;i < n;i++)    cout<<ans[i]<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_30277239/article/details/88929423
111