农民约翰正在指挥他的N头牛进行清理工作。
他将一天划分为了T个班次(1~T)。
每头牛都只能在一天中的某一个时间段内进行不间断的工作。
你需要帮助约翰排列出一个合理的奶牛的清理班次,使得每个班次都有奶牛在进行清理,而且动用的奶牛数量可以尽可能的少。
输入格式
第1行:两个空格隔开的整数N和T。
第2..N+1行:第i+1行包含两个整数,分别表示第i头牛可以进行工作的开始时间和结束时间。
输出格式
输出一个整数,表示在每个班次都有奶牛清理的情况下,所需的奶牛最小数量。
如果无法做到每个班次都有奶牛清理,则输出-1。
数据范围
1≤N≤25000,
1≤T≤106
输入样例:
3 10
1 7
3 6
6 10
输出样例:
2
思路:本题可以转化为求:将[1,t]这个区间完全覆盖的最小的区间个数
状态表示:
用f[i]表示在用i位置左边的区间将[1,i]覆盖的所有方案中的最小区间个数
状态计算:
将集合按最后一个区间的不同划分为若干子集,因为最后一个区间可以看成是固定的,只需要区分动态的前面的已经覆盖的区间的右端点k,且k的范围为[l-1,r].
所以取这些子集的min即为以当前最后一个区间为[l,r]的方案的覆盖的最小个数,即:f[r]=min(f[k])
因为数据范围较大,所以可以用线段树来维护覆盖一个区间的最小区间个数。
注:初始时,将个数设为inf,如果某一点i不是任何一个方案的最后一个区间的右端点,说明没有覆盖到当前i的方案,所以无解。
完整代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=1e6+5,inf=1e8;
struct Range
{
int l,r;
bool operator< (const Range &t)const
{
return r<t.r;
}
}range[maxn];
struct
{
int l,r,v;
}tree[maxn<<2];
int n,t;
void pushup(int p)
{
tree[p].v=min(tree[p<<1].v,tree[p<<1|1].v);
}
void build(int p,int l,int r)
{
tree[p]={l,r,inf};
if(l==r) return;
int mid=l+r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
}
void update(int p,int k,int v)
{
if(tree[p].l==tree[p].r){
tree[p].v=min(tree[p].v,v);
return;
}
int mid=tree[p].l+tree[p].r>>1;
if(k<=mid) update(p<<1,k,v);
else update(p<<1|1,k,v);
pushup(p);
}
int query(int p,int l,int r)
{
if(tree[p].l>=l&&tree[p].r<=r){
return tree[p].v;
}
int mid=tree[p].l+tree[p].r>>1;
int res=inf;
if(l<=mid) res=query(p<<1,l,r);
if(r>mid) res=min(res,query(p<<1|1,l,r));
return res;
}
int main()
{
cin>>n>>t;
build(1,0,t);
for(int i=0;i<n;i++){
cin>>range[i].l>>range[i].r;
}
sort(range,range+n);
update(1,0,0);
for(int i=0;i<n;i++){
int l=range[i].l,r=range[i].r;
int v=query(1,l-1,r-1)+1;//dp当前v=min(f(k)) k从l-1到r-1,再加上当前[l,r]这一个
update(1,r,v);//用覆盖当前[1,r]的最小区间个数v(f[r]=v),更新覆盖r点的区间的信息
}
int res=query(1,t,t);
if(res==inf) cout<<-1<<endl;
else cout<<res<<endl;
return 0;
}