python算法题——搜索篇 poj1011棒

文章描述了一种数学问题,涉及到将棍子切割后再恢复原始状态。提出了使用二分搜索和深度优先搜索(DFS)的算法来确定最小原始长度,通过处理切割后的棍子长度列表。文章提供了一个错误的DFS实现,并指出了越界问题,给出了修正的建议。
摘要由CSDN通过智能技术生成

描述

乔治拿起相同长度的棍子随机切割,直到所有部分最多长 50 个单位。现在他想把棍子恢复到原来的状态,但他忘记了自己原来有多少根棍子,也忘记了原来有多长。请帮助他设计一个程序,计算这些棍子的最小原始长度。所有以单位表示的长度都是大于零的整数。

输入

输入包含 2 行的块。第一行包含切割后的棍子零件数量,最多有64根棍子。第二行包含由空格分隔的那些部分的长度。文件的最后一行包含零。

输出

输出应包含尽可能小的原始摇杆长度,每行一个。

示例输入

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

示例输出

6
5

新加一组数据

64
40 40 30 35 35 26 15 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 43 42 42 41 10 4 40 40 40 40 40 40 40 40 40 40 40 40 40 40 25 39 46 40 10 4 40 40 37 18 17 16 15 40 40 40 40 40 40 40 40
​
//答案:
第1 of 5根木棍:
46+43+42+42+41+40+40+40+40+40+40=454
第2 of 5根木棍:
40+40+40+40+40+40+40+40+40+40+40+10+4=454
第3 of 5根木棍:
40+40+40+40+40+40+40+40+40+40+40+10+4=454
第4 of 5根木棍:
40+40+40+40+40+40+40+40+40+40+39+15=454
第5 of 5根木棍:
40+40+40+40+40+37+35+35+30+26+25+18+17+16+15=454
​
454
​
46
40 37 32 10 47 4 42 56 61 23 59 36 27 16 16 37 26 19 14 29 31 58 51 32 63 28 11 25 12 15 39 42 46 43 11 19 53 17 39 21 45 44 8 23 51 55
​
58
57 6 44 4 16 35 54 9 32 23 43 55 46 41 8 41 55 44 31 59 57 58 59 29 53 30 3 39 52 17 32 45 8 40 34 18 20 11 32 33 14 41 31 25 4 42 54 9 29 37 47 29 34 20 47 56 61 5
​
26
3 64 18 49 4 40 18 61 50 36 17 49 8 17 62 11 24 8 36 59 34 26 28 7 37 26 0
​
答案是:
89
89
99
​

给出一个数字代表现在小段的个数,然后再给出所有小段的长度,求可能的最短木棍长度

输入9,521521521可以是6=222、6=51、6=61、6=51

原先的棍子长度是不确定的,最长是sum(all),最短是max(all) + 1

由以上分析可知这题可以使用二分策略(应该,但是这是搜索题单,没事,先写再说

二分

l = max(sticks) + 1
r = sum(sticks)
while r - l != 1:
    m = (l + r)//2
    if check(m):
        r = m
    else:
        l = m

check

def check(m):
    #首先需要确定的一点就是原先的长度都是一样的,那么这个m应该是可以整除sum(sticks)的
    #但是这里有一个明显的问题,就是仅仅知道不能整除的话不能确定是什么范围
    if sum(sticks) % m != 0:
        return ???

这里或许可以提前将可能的值列出来,而用不着二分,因为本题的难点在于如何确定这个长度是可以被切割成上述小段的。

s = sum(sticks)
lengths = []
for i in range(max(sticks) + 1, s + 1):
    if s % i == 0:
        lengths.append(i)

这样就把可能的值加到了lengths数组里面。

那么接下来的check就是检验这个长度是否可以满足要求了

check

def check(l):
    num = s//l #组数
    n_stick = n/num #每组的根数,有可能不为整数
    #已知条件还有:总小根数n、每根的长度sticks
    #没想出来

下面看看搜索大法

所以这里的DFS就是一种check,我们之前将可能的length拿出来相当于是一种剪枝思路了。

这里我们主要来看这个组合过程,先将所有的木棍排序,然后把其中最大的拿出来,标记为使用过了,然后一个一个向后遍历,其中注意剪枝的细节。

DFS

def DFS(stick, leng, pos): #leng表示当前已有的长度,stick表示已经合成了几根
    #pos表示搜索到第几根
    if leng == 0:
        sign = True
    else:
        sign = False
    if stick == num: #表示已经拼接完成所有的棍子,成功
        return True
    for i in range(pos + 1, n):
        if vis[i]: #如果棍子用过,跳过
            continue
        if leng + sticks[i] == aim: #leng加上这根木棍的长度刚刚好够到达aim
            vis[i] = True
            if DFS(stick + 1, 0, -1):#继续搜索下一根,成功的话返回True
                #由于这里的True必须得要是 最后一根 全部完成才会返回,否则是回溯上去到
                #下一个i尝试不同的stick
                return True
            vis[i] = False #如果上述没有成功,则不适用这一根
            return False
        elif leng + sticks[i] < aim:
            vis[i] = True
            if DFS(stick, leng + sticks[i], i):
                return True
            vis[i] = False
            if sign: #如果当前是在合成阶段,则不去干扰他,如果当前leng为0,再加上上面都没有返回True以至于他走到了这里,那么就说明是不可行的
                return False
            while i < n - 1 and sticks[i] == sticks[i + 1]:#剪枝,这里如果和之前的stick长度一样的话就没必要再去比了
                i += 1
        #如果加上了这根棍子既不是刚好等于也不是小于aim,则返回False
    return False

Main

if __name__ == '__main__':
    n = int(input())
    sticks = map(int, input().split())
    sticks.sort(reverse = False)
    for aim in range(sticks[0] + 1, sum(sticks) + 1):
        if sum(sticks) % aim == 0:
            num = sum(sticks) // aim
            vis = [False for j in range(n)]
            if dfs(1, 0, -1):
                print(aim)
                break

while那里的i + 1会越界,我直接将整个代码和报错给chatGPT看,一下子就找出来了,真是牛。

以下是它的回答:

这个错误是由于在while sticks[i] == sticks[i + 1]:这一行中,i + 1可能会超出sticks的范围。当i等于n-1(即已经到达sticks数组的末尾)时,尝试访问sticks[i + 1]将导致IndexError

要解决这个问题,你需要确保在尝试访问sticks[i + 1]之前检查i是否小于n - 1。可以在while循环的条件中添加这个检查。修改后的代码如下:

pythonCopy codedef DFS(stick, leng, pos):  # leng表示当前已有的长度,stick表示已经合成了几根
    ...
        elif leng + sticks[i] < aim:
            vis[i] = True
            if DFS(stick, leng + sticks[i], i):
                return True
            vis[i] = False
            if sign:  # 如果当前是在合成阶段,则不去干扰他,如果当前leng为0,再加上上面都没有返回True以至于他走到了这里,那么就说明是不可行的
                return False
            while i < n - 1 and sticks[i] == sticks[i + 1]:  # 剪枝,这里如果和之前的stick长度一样的话就没必要再去比了
                i += 1
        # 如果加上了这根棍子既不是刚好等于也不是小于aim,则返回False
    return False

这样,当i等于n - 1时,将不会尝试访问sticks[i + 1],从而避免了IndexError

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值