对分查找的最多次数_对分查找最优解问题分析

eeebd24fb11f2ce8291b576f258b1b1c.png 说在前面

对分查找算法是分治策略的一个典型应用,在历届选考真题卷中都能看到它的身影,也是各种联考和模拟卷的“宠儿”。由于考得太多,常规的对分查找题目已经不能“考倒”学生,一些披着“对分查找”外衣的复杂算法如雨后春笋般冒出来,疯狂地磨砺学生的思维,摧残老师的头发。

“2018学年第一学期慈溪市高三技术期末卷16题”曾经让众多师生摸不着头脑,“2019年11月三地市教学质量检测试卷第12题”更是让人做得云里雾里。

2019年11月三地市教学质量检测试卷第12题解析

题目

999bc412a69c2817d929f6194a164d15.png

考查知识点

对分查找、数组元素求和。要求学生熟悉数组的的基本操作,深刻理解对分查找算法思想,能够利用对分查找算法寻找最优解。

三解析

本题是用对分查找算法求k段元素最大和的最小值问题。因为题目提供的代码有些难以读懂,我们先自己编写代码来解决此问题。

为了更好的理解问题,我们把代码模块化处理,可以模仿“2018学年第一学期慈溪市高三技术期末卷16题”的做法,自定义函数check(n, k, ans)来判断把长度为n的数组最多分成k段以后,各段和是否都不大于ans,若是返回真,否则返回假。

函数返回真,说明ans不小于k段元素最大和,是满足条件的解。我们采用线性查找的方法,从小到大逐个猜测ans的值,很明显第一个满足条件的解就是最优解。代码如下:

Private Function check(n As Integer, k As Integer, ans As Integer) As Boolean

    t = 0: s = 1 '默认至少分成1段

    For i = 1 To n

        If t + a(i) <= ans Then

            t = t + a(i)

        Else '第s段元素和大于ans,则另起一段

            s = s + 1

            t = a(i)

        End If

    Next i

    check = (s <= k) '若ans比较大,分段数量也可能小于k

End Function

Private Sub Command3_Click()

    Dim n As Integer, k As Integer, i As Integer

    Dim L As Integer, R As Integer, m As Integer

    a(1) = 1: a(2) = 2: a(3) = 3: a(4) = 3: a(5) = 1

n = 5: k = 3: L = 3: R = 10

'从小到大依次查找k段元素最大和,首次满足条件的解即最优解(最小值)

    For m = L To R

        If check(n, k, m) Then Exit For

    Next m

    Label1.Caption = "最小值:" & m

End Sub

当然,我们可以采用效率更高的对分查找算法,对分枚举m的值,根据check()函数的反馈来调整m的值。若m太小,不能得到解,则增大m的值;若m较大,可以得到一个解,则将其存储到ans中,并继续减小m,直到获得最优解。相关代码如下:

Private Sub Command4_Click()

    Dim n As Integer, k As Integer, ans As Integer

    Dim L As Integer, R As Integer, m As Integer

    a(1) = 1: a(2) = 2: a(3) = 3: a(4) = 3: a(5) = 1

    n = 5: k = 3: L = 3: R = 10: ans = R

    Do While L <= R

        m = (L + R) \ 2  '对分枚举m的值

        If check(n, k, m) Then

            ans = m '找到一个解,但不一定是最优解

            R = m - 1 '减小m的值

        Else

            L = m + 1 '增大m的值

        End If

    Loop

    Label1.Caption = "最小值:" & ans

End Sub

上述代码模仿了“2018学年第一学期慈溪市高三技术期末卷16题”的做法,虽然思路比较清晰,但是代码不够简洁。由于程序本身需要实现的功能不多,完全可以省略自定义函数check(),把所有的代码都集中在一个函数中。改进后的代码如下:

Private Sub Command5_Click()

    Dim n As Integer, k As Integer, i As Integer

    Dim L As Integer, R As Integer, m As Integer

    Dim s As Integer, t As Integer

    a(1) = 1: a(2) = 2: a(3) = 3: a(4) = 3: a(5) = 1

    n = 5: k = 3

    L = 3: R = 10

    Do While L < R

        m = (L + R) \ 2 '因为最优解是满足条件的最小值,为避免死循环,m左偏

        t = 0: s = 1 '默认至少分成1段

        For i = 1 To n

            If t + a(i) <= m Then

                t = t + a(i)

            Else '第s段元素和大于ans,则另起一段

                s = s + 1

                t = a(i)

            End If

        Next i

        If s <= k Then'm满足条件,继续寻找比m更小的最优解

            R = m '最优解在[L,m]之间

        Else

            L = m + 1 '最优解在[m+1,R]之间

        End If

    Loop

    '循环结束后L=R

    Label1.Caption = "最小值:" & L

End Sub

以上是笔者能想到的相对直观的代码。但是这些代码都与题目提供的代码不一样!那么如何来理解题目中的代码呢?

说实话,真的很难!笔者费了九牛二虎之力总算稍微理顺了思路(但是仍不敢说真正领会了出题者的意图),下面尝试通过添加注释的方式对原题目代码做一些分析:8101c08335b4e3bef4edffca82c7539a.png

上述代码最令人费解的地方就是没有直接把m作为第s段元素和的最大值,而是把(m-1)作为和的最大值来处理了,所以它的do while循环条件和内层循环的if条件都不合常理。此处故意挖坑,人为增加了阅读障碍(当然也可能是出题者另有深意,而我未能领会)。

当然,如果仅仅是为了找到正确选项,我们无需完全理解每一句代码的含义,可以采用排除法和特殊值法等技巧来寻找答案。

在本题中,虽然m的含义不好理解,但是s和k的含义还是很明确的,当s<=k时,m是满足条件的解,我们可以进一步减小右边界,反之则增大左边界,故可以排除B、C和D。

为了确保万无一失,我们还可以把数据代入,模拟运行一遍程序。

把选项A的代码代入程序,运行过程如下:

L

R

m

s

k

3

10

6

3

3

3

6

4

4

3

4

6

5

3

3

4

5

循环结束,输出最优解L

答案

A

五拓展思考

解析中涉及到的2道题目和普通对分查找算法的区别在于:普通对分查找算法都是试图用平均值m去指向待查找的元素(满足条件的解),一旦找到该元素则马上跳出循环;而我们今天研究的对分查找变例不仅仅要找到满足条件的解,而且要找到最优解,因此不能中途跳出循环,而是要等到循环正常结束后才能确认找到的解为最优解。

虽然对分查找算法的基本框架是固定的,但是实现同一功能的代码有多种写法,只要稍微修改一下循环条件,或对平均值m采取不同的取整方式,就可以写出不同的代码。所以当题目给出的代码与我们预想的不一致时,要尽量站到出题者的角度去理解代码。实在不能理解的时候,可以通过代入具体的数据来模拟运行程序,找出正确答案。

我在这里再给出3道题目,希望大家通过相关练习能增强对此类题目的理解。

第1题:最小距离最大值问题。数组a(n)是一个长度为n+1的递增正整数序列(其中a(0)=0),求从数组中删除m个元素以后(不能删除a(0)),剩下的元素中最小距离的最大值。距离是指当前元素减去前一个元素的差。

例如:当a = (0,2,11,14,17,21,25),m = 2时,返回4。

分析过程:如果删除元素“2”和“11”,剩下的元素为(0,14,17,21,25),最小距离为3,即17-14;如果删除元素“2”和“14”,剩下的元素为(0,11,17,21,25),最小距离为4,即21-17 或者 25-21。

依次分析删除2个数的所有可能情况,可知最小距离的最大值为4。

下面的代码使用对分查找算法求最小距离的最大值,请把缺失的代码补充完整:

Private Sub Command1_Click()

    Dim left As Integer, right As Integer, mid2 As Integer

    Dim cnt As Integer, i As Integer, k As Integer

    left = 1: right = a(n)

    Do While left < right

        '因为最优解是满足条件的最大值,为避免死循环,mid右偏

mid = (left + right + 1) \ 2

        cnt = 0: k = 0

        For i = 1 To n '当前距离小于mid,删除元素a[i]

            If a(i) - a(k) >= mid Then k = i Else ①      

        Next i

        If cnt <= m Then 'mid满足条件,继续寻找比mid更大的最优解

            left = ②         '最优解在[mid,right]之间

        Else

            right = ③           '最优解在[left,mid-1]之间

        End If

    Loop

    List1.AddItem Str(m) + ":" + Str(right)

End Sub

第2题:给定n(n为奇数且小于1000)个整数,整数的范围在[1,m]之间,请使用二分法求这n个整数的中位数。所谓中位数,是指将这n个数排序之后,排在正中间的数。

例如:当a = (8,6,6,3,4,8,1)时,中位数为6。

下面的代码使用对分查找算法求中位数,请把缺失的代码补充完整:

Private Sub Command2_Click()

    Dim a(1 To 1000) As Integer

    Dim n As Integer, m As Integer, i As Integer

    Dim left As Integer, right As Integer

    Dim count As Integer, mid As Integer

    n = Val(Text1.Text): m = Val(Text2.Text)

    For i = 1 To n

        a(i) = ①            '生成范围在[1,m]之间的随机整数

    Next i

    left = 1: right = m

    Do While left <= right

        mid = (left + right) \ 2

        count = 0

        For i = 1 To n '记录不小于mid的整数数量

            If a(i) >= mid Then count = count + 1

        Next i

        If count > n \ 2 Then

            left = ②          

        Else

            right = ③          

        End If

    Loop

    Label1.Caption = "中位数:" & right

End Sub

第3题:对分查找插入排序算法。

Private Sub Command7_Click()

    Dim n As Integer, i As Integer, L As Integer, R As Integer, key As Integer

    n = 10

    For i = 1 To n '产生n个随机整数赋值给数组a并显示在list1中

        a(i) = Int(Rnd * 10) + 1

        List1.AddItem Str(a(i))

    Next i

    For i = 2 To n

        key = a(i)

        L = 1: R = i - 1

        Do While L <= R

            m = (L + R) \ 2

            If a(m) <= key Then L = ①       Else R = ②       

        Loop

        For j = i - 1 To L Step -1

            a(j + 1) = a(j)

        Next j

        a(L) = ③         

    Next i

    For i = 1 To n '排序结果显示在list2中

      List2.AddItem Str(a(i))

    Next i

End Sub

六拓展思考答案

(1) ① cnt = cnt + 1

② mid

③ mid – 1

(2) ① Int(Rnd * m + 1)

② mid + 1

③ mid – 1

(3) ① m + 1

② m - 1

③ key

写在后面

为了保证解析的原创性和思维的独特性,我都是独立解题后,先不看答案(除非题目不会做),直接把解析写好,再去看答案。

当然,如果发现参考答案有更好的思路,我还是很乐于学习和借鉴的。同时,由于本人水平有限,解析中难免出现疏漏甚至错误之处,敬请谅解。

无论是赞同还是反对我的看法,都请你给我留言。如果你有新的想法,千万不要憋在心里,请发出来大家一起讨论。让我们相互学习,共同进步!

需要本文word版的,可以加入“选考VB算法解析”知识星球参与讨论和下载文件,“选考VB算法解析”知识星球汇集了数量众多的同好,更多有趣的话题在这里讨论,更多有用的资料在这里分享。

我们专注选考VB算法,感兴趣就一起来!

61add5a662b8323b9b19f60a36926297.png

相关优秀文章:

阅读代码和写更好的代码

最有效的学习方式

选考VB算法解析之2018年11月高考真题卷第17题

选考VB算法解析之2017年11月高考真题卷第17题

最小距离最大值问题

插入排序算法及其变例分析

9049a328d294fb88cd2d37fd56d25438.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值