信息学奥赛一本通 1324:【例6.6】整数区间

【题目链接】

ybt 1324:【例6.6】整数区间

【题目考点】

1. 贪心
2. 贪心选择性质的证明

要想证明贪心选择可以得到最优解,只需要证明最优解包含每一次的贪心选择。
使用数学归纳法:

  1. 证明最优解包含第一次的贪心选择
  2. 假设存在最优解包含前k次的贪心选择,证明该最优解包含第k+1次的贪心选择

【解题思路】

每次选择右端最小的区间的右端数字,并删掉所有包含该数字的区间。
用数学语言说,现有区间: e 1 = [ l 1 , r 1 ] , e 2 = [ l 2 , r 2 ] , . . . , e n = [ l n , r n ] e_1=[l_1,r_1], e_2=[l_2,r_2],...,e_n=[l_n,r_n] e1=[l1,r1],e2=[l2,r2],...,en=[ln,rn]
选择右端点r最小的区间 e m = [ l m , r m ] e_m=[l_m,r_m] em=[lm,rm],删掉所有满足 r m ∈ e x r_m\in e_x rmex的区间 e x e_x ex

1. 贪心选择性质的证明:

贪心选择:每次选择右端最小的区间的右端点数字

  1. 证明:存在最优解包含右端最小的区间的右端点数字

记右端最小的区间为 e m = [ l m , r m ] e_m = [l_m,r_m] em=[lm,rm],证明存在最优解包含 r m r_m rm
使用反证法:假设所有最优解都不包含 r m r_m rm
对于某一最优解,根据题意:“对于每一个区间,都至少有一个整数属于该集合”,所以一定要在区间 e m e_m em中选择一个数字。假设该最优解中选择了区间 e m e_m em中的数字 x x x x ≠ r m x \neq r_m x=rm,包含x的区间有p个,组成的集合为: E x = { e x 1 , e x 2 , . . . , e x p } E_x = \{e_{x1},e_{x2},...,e_{xp}\} Ex={ex1,ex2,...,exp},选择x后,这些区间都被删掉,不再被考虑。
现在考虑在该最优解中用 r m r_m rm替换 x x x,包含 r m r_m rm的区间有q个,组成集合为: E r = { e r 1 , e r 2 , . . . , e r q } E_r=\{e_{r1},e_{r2},...,e_{rq}\} Er={er1,er2,...,erq},由于 e m e_m em是右端最小的区间,所以 E x E_x Ex中的所有区间的右端都大于等于 r m r_m rm,也就是说 r m r_m rm E x E_x Ex的每个区间中,亦即 E x ⊆ E r E_x\subseteq E_r ExEr。选择 r m r_m rm后, E r E_r Er中的所有区间都被删除,包括了 E x E_x Ex中的所有区间。该最优解中其他数字不变,仍然可以形成一组最优解。而这组最优解包含了 r m r_m rm,与假设相悖。因而原命题得证。

  1. 证明:在k次贪心选择后,存在最优解包含这一次的贪心选择:剩余的区间中右端最小的区间的右端点数字

证明方法与证明第1点相似,不再赘述。

2. 具体做法

将所有区间排序,排序规则为:右端小的排在前面;如果右端相同,那么左端小的排在前面。
按顺序遍历各个区间,选择当前区间的右端点并记录,选择的数字个数加1。
继续遍历,如果当前区间的左端点小于等于之前记录的右端点,那么略过这个区间。
如果当前区间左端点大于之前记录的右端点,那么选择当前区间的右端点并记录,选择的数字个数加1。
重复上述过程,直至遍历完所有区间。

【题解代码】

解法1:贪心
#include<bits/stdc++.h>
using namespace std;
struct Sec
{
	int l, r;//l:区间左端点 r:区间右端点 
};
bool cmp(const Sec &a, const Sec &b)
{//右端点更小的排在前面,右端点相同则左端点更小的在前面 
    if(a.r == b.r)
        return a.l < b.l;
    else
        return a.r < b.r;
}
int main()
{
    Sec s[10005];
   	int n, ct = 0, lastR = -1;//lastR:上一个选择的右端点 ct:选择的数字个数
   	cin >> n;
	for(int i = 1; i <= n; ++i)
   		cin >> s[i].l >> s[i].r;
   	sort(s+1, s+1+n, cmp);
	for(int i = 1; i <= n; ++i)
	{
	    if(s[i].l > lastR)
	    {
	       ct++;
           lastR = s[i].r;   
        }
    }
    cout << ct;
    return 0;
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值