题意:给出区间[ 1,T ]和N个小区间,要求用尽可能少的小区间覆盖区间[ 1,T ],输出最少的小区间数量;若不能覆盖,输出-1。
思路:贪心。
具体:令begin为当前未被覆盖的区间起点。
贪心策略:选取包含点begin的区间中右端点最大的那个;若不存在包含begin的区间,输出-1。
证明:因为begin为未被覆盖的区间起点,所以begin一定要被小区间覆盖,将最优解中覆盖begin的小区间命名为区间X。
以下使用剪贴技术证明。
若存在包含begin的小区间Y( Y != X )使最终所需的小区间数少于原最优解,那么Y一定包含了X之后的某些小区间,
即Y的右端点大于X的右端点,与贪心策略矛盾,得证。
#include <iostream>
#include <algorithm>
using namespace std;
const int Max = 25000;
// 对象
int N, T;
struct Interval {
int b, e;
}I[Max];
// 函数
bool greater( const Interval & i1, const Interval & i2 )
{
return i1.b < i2.b;
}
int Greedy()
{
int num = 0; // 最少区间数
int right = 0; // 已覆盖区间右端点
int top = 0; // 集合首
while( right < T ) {
// 选出包含begin的区间中右端点最大的那个
int begin = right + 1; // 待覆盖的起点
for( int i = top; i < N; ++ i ) {
if( I[i].b <= begin && I[i].e >= begin )
right = I[i].e > right ? I[i].e : right;
else if( I[i].b > begin ) {
top = i;
break;
}
}
// 没有包含begin的区间
if( begin > right ) break;
else ++ num;
}
if( right == T ) return num;
else return -1;
}
int main()
{
scanf( "%d%d", & N, & T );
for( int i = 0; i < N; ++ i )
scanf( "%d%d", & I[i].b, & I[i].e );
sort( I, I + N, greater );
printf( "%d\n", Greedy() );
return 0;
}