线段区间的覆盖
用i表示x轴上坐标为[i-1,i]的区间(区间长度为1),并给出M个不同的整数来表示M个这样的区间。现在要求画出几条线段覆盖住所有的区间,条件是:每条线段可任意长,但要求所画线段长度之和最小,并且线段的数目不超过N。
举例:给出M=6,1,2,4,5,7,11,分别表示6个长度为1的区间,要求用不超过N=3条线段将其覆盖。
上图给出了一种可行的覆盖方案,用三段长线段覆盖住6个长度为1的小线段,使得3短线段长度之和最小。
l 算法思想
运用贪心算法,M个线段会产生M-1个间断,间断有大有小,按照从大到小的顺序把线段间的间隔排好。假设初始状态下有一整条线段覆盖整个区域,每一步都从间隔最大的位置上断开该线段,直至断开N-1次,此时一整条线段就被分成了N截且这N截线段的长度和最小。
l 程序模型的建立与设计流程
如果N>=M,显然用M条长度为1的线段就可覆盖所有区间,所求的线段总长度也就为M。如果N,要另想办法。
① N=1,那么所需线段总长为position[M-1]-position[0]+1;
② N=2,那么相当于把①中的长线段分成两截。找到最大距离distance[i]=position[i]-position[i-1]-1 (1
③ N=3,同理,将distance第二大的位置断开,于是便形成3截线段,并且长度和最小。
④ N=k(k>1)时,只要在N=k-1时最小总长的覆盖方案下,找到被同一条线段覆盖最大的两个区间,“贪心”地从间隔处断开并适当调整两条线段的端点,就可以得到总长最小的方案。
l 数据结构的选用
可用整型数组position[M]表示所有的区间,并假设position[M]已按从小到达的顺序排好了。
l 源程序编码清单
#include
using namespace std;
const int SIZE=200;
void Sort(int value[],int nNumber) //对nNumber个值从大到小排列
{
for(int i=0;i
for(int j=0;j
if(value[j]
{
int temp=value[j];
value[j]=value[j+1];
value[j+1]=temp;
}
}
void main()
{
int M=0;
int position[SIZE];
cout<<"请输入待覆盖的区间总数M="<<endl;
cin>>M;
int i=0;
cout<<"请具体地输入第"<<M
<<"个要覆盖的区间:"<<endl;
for(i=0;i
{
cin>>position[i];
}
Sort(position,M);
int distance[SIZE-1];
for(i=0;i
distance[i]=position[i]-position[i+1]-1;
Sort(distance,M-1);
int N=1; //可用于覆盖的线段总数
cout<<"请输入可用于覆盖的线段总数N="<<endl;
cin>>N;
//**********贪心算法*************************
if(N>=M)
{
cout<<"最小的线段总长为"
<<M<<endl;
return;
}
else if(N
{
int nLine=1; //记录当前线段总数
int nTotalLength=position[0]-position[M-1]+1;//记录当前情况下所用线段的总长
int nDivide=0; //记录当前最大的未断开的区间位置
while((nLine0]))
{
nLine++;
nTotalLength-=distance[nDivide];
nDivide++;
}
cout<<"最小的线段总长为"
<<nTotalLength<<endl;
}
}
l 程序输入、输出
l 时间与空间复杂度分析
算法时间复杂度为O(nlogn)+O(n)。
l 程序使用说明
① 对position的排序无论从大到小还是从小到大没有区别,不影响区间距离的计算。
② 注意while循环的两个条件
l 总结与完善
这个程序可以得到最小总长条件下所需的最少线段的数目,但并不能给出这些线段的端点信息。可以进一步对程序修改以添加此功能。