贪心算法

本文探讨了区间覆盖问题的几种类型,包括区间完全覆盖问题、最大不相交覆盖问题及区间选点问题。针对每种问题提供了详细的解题思路与算法实现步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 事件序列问题


 (1)区间完全覆盖问题
问题描述:
给定一个长度为m的区间,再给出n条线段的起点和终点(闭区间),求最少使用多少条线段可以将整个区间完全覆盖?
样例:区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]。
解题过程:
1、将每一个区间按照左端点递增顺序排列完后为[1,4],[2,4],[2,6],[3,5],[3,6],[3,7],[6,8]
2、设置一个变量表示已经覆盖到的区域。在剩下的线段中找出所有左端点小于等于当前已经覆盖到的区域的右端点的线段中,右端点最大的线段再加入,直到已经覆盖全部的区域

3、过程:假设第一步加入[1,4],那么下一步能够选择的有[2,6],[3,5],[3,6],[3,7],由于7最大,所以下一步选择[3,7],最后一步只能选择[6,8],这个时候刚好达到了8退出,所选区间为3。

 (2)最大不相交覆盖(算法不全?答案不唯一?和会议安排问题一样? 直接按照终点排序再依次选择?)
问题描述:给定一个长度为m的区间,再给出n条线段的起点和终点(开区间和闭区间处理的方法不同,这里以开区间为例),问题:从中选取尽量多的线段,使得每个线段都是独立的(即不和其它有任何线段有相交的地方)。
样例:区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]。
解题过程:
对线段的右端点进行升序排序,每加入一个线段,然后选择后面若干个(也有可能是一个)右端点相同的线段,选择左端点最大的那一条,如果加入以后不会跟之前的线段产生公共部分,那么就加入,否则就继续判断后面的线段。
1、排序:将每一个区间按右端点进行递增顺序排列完后为[1,4],[2,4],[3,5],[2,6],[3,6],[3,7],[6,8]。
2、第一步选取[2,4],发现后面只能加入[6,8],所以区间的个数为2
——实际从数量来看,[1,4]、[6,8] 和[3,5]、[6,8]也是答案,区间的个数都为2。

3、贪心证明:因为需要尽量多的独立的线段,所以每个线段都尽可能的小,对于同一右端点,左端点越大,线段长度越小。那么为什么要对右端点进行排序呢?如果左端点进行排序,那么右端点是多少并不知道,那么每一条线段都不能对之前所有的线段进行一个总结,那么这就明显不满足贪心的最有利结构了。

3)区间选点问题
问题描述:给定N个区间[a,b],取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以重复)。
样例:区间[a1, b1] 、[a2, b2]、 [a3, b3]
解题过程:将每个区间按照右坐标进行递增排序,相同右坐标的按照左坐标从大到小排列。

实际案例:某街道有n段区间,每段区间至少要有k个广告牌,要求你在这些区间放置广告牌,但是广告牌的数量要是最少的,而且将其位置输出来。



二 区间覆盖问题

用i来表示x轴上坐标为[i-1,i]的区间(长度为1),并给出M(1=<M=<200)个不同的整数,表示M个这样的区间。现在让你画几条线段覆盖住所有的区间,条件是:每条线段可以任意长,但是要求所画线段长度之和最小,并且线段的数目不超过N(1=<N=<50)。
    例如:M=5个整数1、3、4、8和11表示区间,要求所用线段不超过N=3条。

0     1     2    3      4      5      6      7       8       9     10     11



如果N>=M,那么显然用M条长度为1的线段可以覆盖住所有的区间,所求的线段总长为M。
如果N<M,则:
如果N=1,那么显然所需线段总长为:用一条线段覆盖住所有区间。线段总长:M。
如果N=2,相当于N=1的情况下从某处断开(从哪儿断开呢?)——找到间隔最大的两个相邻区间,将之断开。
...

HDOJ_1050  Moving Tables

#include <iostream> 
using namespace std; 
int main() 
{ int t,i,j,N,P[200];
   int s,d,temp,k,min; 
   cin>>t; 
   for(i=0;i<t;i++) 
      { 
         for(j=0;j<200;j++) 
              P[j]=0; 
         cin>>N; 
     for(j=0;j<N;j++) 
            { 
               cin>>s>>d; 
               s=(s-1)/2; 
               d=(d-1)/2

if(s>d) 
                  { temp=s; 
                     s=d; 
                     d=temp; } 
               for(k=s;k<=d;k++) 
                  P[k]++; 
            } 
         min=-1; 
         for(j=0;j<200;j++) 
            if(P[j]>min) 
               min=P[j]; 
         cout<<min*10<<endl; 
      } 

   return 0; 

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值