区间覆盖问题

一、设计题目

问题描述:

设x1,x2,…,xn是实直线上的n个点。用固定长度的闭区间覆盖这n个点,至少需要多少个这样的固定长度闭区间?设计解此问题的有效算法,并证明算法的正确性。

算法设计:

对于给定的实直线上的n个点和闭区间的长度k,计算覆盖点集的最少区间数。

数据输入:

由文件input.txt给出输入数据。第1行有2个正整数n和k,表示有n个点,且固定长度闭区间的长度为k,接下来的1行中,有n个整数,表示n个点在实直线上的坐标(可能相同)。

结果输出:

输入文件示例             输出文件示例

input.txt                output.txt

    7 3                      3

    1 2 3 4 5 -2 6

二、程序功能简介

得到n个数值表示直线上的点,其中n个数值可能会相同,用一个固定长度为k的区间将这n个数值的点覆盖,求解覆盖所有点所需要的区间数目。

三、主体内容

3.1设计分析

从题目以及测试数据可以得到大概思路:先将n个点进行从小到大排序,并放置到x轴上,然后问题就可以转换为用长度为k的区间覆盖x轴上的所有点。如下图1所示:

                                                                                                        图1

在区间覆盖的时候,存在着许多种情况,但为了保证所用区间数目尽量少,那么就要尽量保证区间端点与被覆盖点重合。那么,就可以假设第一个覆盖区间的端点为n个点中的最左边Xmin,然后在第一个区间之后继续寻找被覆盖点,作为第二个覆盖区间端点,这样直到n个点中最右边Xmax也被区间覆盖,结束运算。如图2-1、2-2、2-3:

                                                                                                          图2-1

                                                                                                          图2-2

                                                                                                         图2-3

3.2程序结构

                        

图3.2-1代码整体思路图
 图3.2-2 minCover()函数图

                                          

3.3各模块的功能及程序说明

模块1:从文件中获取数据

/**********************************/

int n, k;                  

ifstream infile("input.txt");  

infile>>n;                 

infile>>k;                 

int *num=new int[n];       

for(int i=0; i<n; i++)         

    infile>>num[i];

程序说明:读入文件"input.txt"需与源代码文件在同一个文件夹目录下

/************************************/

模块2:调用函数并将函数返回值写入输出文件

/************************************/

ofstream outfile("output.txt");

outfile<<minCover(num, n, k);

程序说明:写入文件"output.txt"需与源代码文件在同一个文件夹目录下

/************************************/

模块3:函数体调用

/************************************/

int minCover(int num[], int n, int k)

程序说明:minCover()函数计算结果并将结果以函数返回值形式返回到主函数

/************************************/

模块4:核心算法

/************************************/

for(int i=0; i<n; i++)  

       if(num[i]-tmp > k)  

       {

           res++;       

           tmp=num[i];      

       }

return res+1;

程序说明:当遍历到当前点与tmp起点位置的距离大于区间长度k时,更新起点tmp,并且在返回时+1,因为最后的退出循环时,i=n点时的区间没在for循环里面计算。

/************************************/

3.4源程序

/******************************************************

题目:  设x1,x2,…,xn是实直线上的n个点。

       用固定长度的闭区间覆盖这n个点,至少需要多少个这样的固定长度闭区间?

       设计解此问题的有效算法,并证明算法的正确性

******************************************************/

#include <iostream>

#include <fstream>

#include <algorithm>

using namespace std;

//minCOver()函数计算结果,其中num[]存储读取的数据

int minCover(int num[], int n, int k)

{

    int res=0, tmp;          //1.定义两个变量res和tmp,res记录区间数目,tmp作为中间值存储每次检索区间时的起点

    sort(num, num+n);    //2.先利用sort()快排函数将数组从小到大排练

    tmp=num[0];              //3.先将最小数作为区间覆盖的第一个点

    for(int i=0; i<n; i++)   //4.遍历每一个点

       if(num[i]-tmp > k)   //5.当遍历到当前点与tmp起点位置的距离大于区间长度k时

       {

           res++;        //6.当遍历到当前点与tmp起点位置的距离大于区间长度k时,区间数目+1

           tmp=num[i];       //7.更新起点tmp

       }

    return res+1;        //8."+1"解释: 因为最后的退出循环时,i=n点时的区间没在for循环里面计算

}

int main()

{

    //1.从input.txt文件里面获取数据

    int n, k;                   //1.1 定义n和k,n表示n个点,k表示固定长度为k

    ifstream infile("input.txt");   //1.2 定义一个输入文件流,以输入方式打开文件"input.txt"

    infile>>n;                  //1.3 从文件"input.txt"里面读取第一个数据作为n

    infile>>k;                  //1.4 从文件"input.txt"里面读取第二个数据作为k

    int *num=new int[n];        //1.5 动态创建一个大小为n的数组

    for(int i=0; i<n; i++)          //1.6 循环文件"input.txt"里面的数据

       infile>>num[i];             //1.7 逐一读取文件"input.txt"里面的数据

 

    //2.将结果存进output.txt文件

    ofstream outfile("output.txt"); //2.1 定义输出文件流对象,打开文件"output.txt"

    outfile<<minCover(num, n, k);   //2.2 将函数minCover()的返回结果存进文件"output.txt"

 

    //3.关闭文件流

    infile.close();                 //3.1 关闭输入文件流

    outfile.close();            //3.2 关闭输出文件流

    return 0;

}

3.5运行结果截图

 

测试数据

输入文件

(input.txt)

输出文件

(output.txt)

1

7 3

1 2 3 4 5 -2 6

3

2

4 2

-6 -3 0 3

4

3

10 2

10 5 3 6 9 2 -1 5 6 7

4

图3.5-1 测试数据1
图3.5-2 测试数据2
图3.5-3 测试数据3

 

3.6设计体会

当我看到题目的时候,感觉题目很简单,有大概思路,但由于考虑到的结果太多,不知道如何进行选择时,就一直卡在那里。因为当区间覆盖最左边的点时,假设区间长度为3,那么长度为3的区间可以将最左边的点覆盖在最左边、也可以是最右边抑或中间。这是长度为3的情况。假如长度为4,那么情况就更多了……所以这个问题一直让我对题目无从下手。最后只能上网参考一下别人的代码,发现是将最左边的点覆盖在最左边是最优的情况,得到思路后就比较容易地写出代码了。

但在证明过程中,由于网上的内容看得更加太深奥了,假如以网上的说法来编写证明过程,这显然是很难符合要求的,所以我就根据自己的理解和想法,把证明以另一种方式来描述。

最后对实训期间所做的内容一个总结:就是让我认识到自己算法能力的薄弱,希望在接下来的大学时光里能够不要落下算法,因为算法是程序的核心。

四、附录

4.1程序流程图

 

   图4.1-2 minCover()函数图
图4.1-1代码整体思路图

 

4.2主要过程列表

如图4.1-1,在main()函数里面获取到n、k、num[]数组内容,然后调用函数minCover()计算结果,将结果以函数返回值返回到main()函数,并写入到输出文件"output.txt"。其中,函数代码逻辑图,如图4.1-2所示,从最左边开始遍历到最右边,当遍历到一个点与tmp值的距离大于固定区间长度时,区间数目+1,直到达到最右边的数值时,并且在退出循环后,res需执行+1操作,因为最后的退出循环时,i=n点时的区间没在for循环里面计算,最后返回结果到main()函数。

4.3程序中的主要变量、函数

int n;                             // n表示n个点

int k;                             // k表示固定长度为k

int *num=new int[n];               //动态创建一个大小为n的数组,存储n个点

ifstream infile("input.txt");      //定义一个输入文件流,以输入方式打开文件"input.txt"

ofstream outfile("output.txt");    //定义输出文件流对象,打开文件"output.txt"

int res=0;                         // res记录区间数目

int tmp;                           // tmp作为中间值存储每次检索区间时的起点

  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值