题目描述:
数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t]( 1<=t<=1,000,000)。
覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。
不可能办到输出-1
思路:
按顺序贪最长有效长度;
直观上来说,选一个区间,一定要让其最大有效长度最大,那么如何判断一个区间的有效长度?假设已经选了i个区间,当选择第i+1区间时,要看第i+1个区间里有多少长度没有被覆盖,这也就是第i+1个区间的有效长度,如果选第i+1个区间时,从左端点到右端点搜索,则时间复杂度是O(n^n);为了在选择时,不对后序的选择产生影响,我们对左端点进行排序,这样就可以顺序的用O(n)的时间遍历所有区间
正确性证明:
假设已经选了k个区间,其覆盖范围是从L到R(假设L>1),则接下来需要选择的区间一定有左端点在[1,L]之间的,假设我们又选择了一个区间,其左端点是LL(1<LL<L),则接下来仍然要选择左端点在[1,LL]之间的区间,依次类推,则一定要选择开头是1的区间;而如果按顺序从1开始选择区间,因为每次选的是有效长度最大的,所以第一个选的区间不会比最优解中从1开始的那个区间的覆盖长度短;假设选了[1,RR],则把RR+1相当于是1,继续重复上述推证,可得到这种方法的解不会比最优解差。
总结:
贪心往往和排序相联系
代码:
1 //贪最长有效长度,按顺序贪,假设已经选了[1,K],下一步一定要选一个起点在[2,K+1]之内的区间,每个区间都有一个有效长度,max_就是这些区间的最长有效长度 2 #include <cstdio> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 struct sec 7 { 8 int l,r,len; //区间的起点,终点和长度 9 bool operator< (const sec &x) const 10 { 11 if(l!=x.l) return l<x.l; //左端点升序,长度降序 12 else if(len!=x.len) return len>x.len; 13 } 14 }a[100005]; 15 int main() 16 { 17 int n,t; cin>>n>>t; 18 for(int i=1;i<=n;i++) 19 scanf("%d %d",&a[i].l,&a[i].r), 20 a[i].len=a[i].r-a[i].l; 21 sort(a+1,a+n+1); 22 int end=0,oldEnd=0,i=1,ans=0; 23 while( end<t ) 24 { 25 int max_=0; 26 i--; 27 while(1) 28 { 29 i++; 30 if( a[i].l<=end+1 ) //计算有效长度 31 { 32 if( a[i].r-end> max_) max_=a[i].r-end; 33 } 34 else break; //当break时,i加到了下次的位置,所以上方i--,并设i的初始值是1 35 } 36 if(max_==0) 37 { 38 cout<<-1<<endl; 39 return 0; 40 } 41 end=end+max_; 42 ans++; 43 } 44 cout<<ans<<endl; 45 }