题目链接:https://www.acwing.com/problem/content/297/
给定区间长度,和n个区间,问最少多少个区间能够覆盖[1,m]长度的区间。
dp状态:到i位置覆盖了[1,i]需要的最少区间数量,转移方程是:先找所有右端点是i的区间,其左右端点分别是l,r,那么f[l-1~r-1]的最小值+1就是最终的结果。通过线段树维护这个f数组可以在O(logm)时间内完成查询以及插入一个值f[i],注意区间是从[0,m]的,最初有f[0]=0,其余都是inf。
代码:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N = 25010,T=1000010,inf=1e8; int n,m; struct Range{ int l, r; bool operator < (const Range& b)const { return r<b.r; } }range[N]; struct node{ int l,r,v; }t[T<<2]; void pushup(int rt){ t[rt].v=min(t[rt<<1].v,t[rt<<1|1].v); } void build(int rt,int l,int r){ t[rt]={l,r,inf}; if(l==r)return; int mid=l+r>>1; build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); } int query(int rt,int L,int R){ if(t[rt].l>=L && t[rt].r<=R)return t[rt].v; int mid=t[rt].l+t[rt].r>>1; int res=inf; if(L<=mid)res=min(res,query(rt<<1,L,R)); if(R>mid)res=min(res,query(rt<<1|1,L,R)); return res; } void update(int rt,int pos,int C){ if(t[rt].l==t[rt].r){ t[rt].v=min(t[rt].v,C); return; } int mid=t[rt].l+t[rt].r>>1; if(pos<=mid)update(rt<<1,pos,C); else update(rt<<1|1,pos,C); pushup(rt); } int main(){ cin>>n>>m;//区间数量以及区间长度 for(int i=1;i<=n;i++)scanf("%d%d",&range[i].l,&range[i].r); sort(range+1,range+n+1); build(1,0,m);//最初的时候f[0]=0 update(1,0,0); for(int i=1;i<=n;i++){ int l=range[i].l,r=range[i].r; int v=query(1,l-1,r-1)+1; update(1,r,v); } int res=query(1,m,m); if(res==inf)res=-1; cout<<res<<endl; return 0; }