用Python实现回溯算法
我正在尝试实现一个算法,这个算法需要输入两个整数 n 和 k,其中 n 是一排座位的数量,k 是想要坐在这排座位上的学生数量。问题是,每个学生之间必须至少相隔两个座位,也就是说,左右两边都要留出两个座位。现在我有一个函数可以生成所有的子集(一个由 0 和 1 组成的数组,1 表示有学生坐在那里),然后我把这个数组传给另一个函数去检查它是否是一个有效的子集。下面是我这个函数的代码:
def process(a,num,n):
c = a.count('1')
#If the number of students sitting down (1s) is equal to the number k, check the subset
if(c == num):
printa = True
for i in range(0,n):
if(a[i] == '1'):
if(i == 0):
if( (a[i+1] == '0') and (a[i+2] == '0') ):
break
else:
printa = False
elif(i == 1):
if( (a[i-1] == '0') and (a[i+1] == '0') and (a[i+2] == '0') ):
break
else:
printa = False
elif(i == (n-1)):
if( (a[i-2] == '0') and (a[i-1] == '0') and (a[i+1] == '0') ):
break
else:
printa = False
elif(i == n):
if( (a[i-2] == '0') and (a[i-1] == '0') ):
break
else:
printa = False
else:
if( (a[i-2] == '0') and (a[i-1] == '0') and (a[i+1] == '0') and (a[i+2] == '0') ):
break
else:
printa = False
if(printa):
print a
else:
return
这段代码在 k 和 n 的值比较小的时候能正常工作,但如果输入的值更高,就会出现“列表索引超出范围”的错误,我不知道为什么会这样。
如果有人能帮忙就太好了,谢谢。
输入的 a 是一个看起来像这样的列表:
['1','0','0','1','0'] # a valid subset for n=5 and k=2
['0','0','0','1','1'] # an invalid subset
编辑:
调用处理函数的代码:
'''
This function will recursivly call itself until it gets down to the leaves then sends that
subset to process function. It appends
either a 0 or 1 then calls itself
'''
def seatrec(arr,i,n,k):
if(i==n):
process(arr,k,n)
return
else:
arr.append("0")
seatrec(arr,i+1,n,k)
arr.pop()
arr.append("1")
seatrec(arr,i+1,n,k)
arr.pop()
return
'''
This is the starter function that sets up the recursive calls
'''
def seat(n,k):
q=[]
seat(q,0,n,k)
def main():
n=7
k=3
seat(n,k)
if __name__ == "__main__":
main()
如果我使用这些数字,就会出现的错误是:
if( (a[i-2] == '0') and (a[i-1] == '0') and (a[i+1] == '0') ):
IndexError: list index out of range
2 个回答
2
一个长度为 n
的数组,它的索引范围是从 0
到 n-1
。所以如果你试图访问 n
,那就是超出了这个列表的范围。
如果你在处理较小的数值时没有发现这个问题,那么生成这些列表的代码可能有错误。
3
我们只需要排除一些不合法的座位安排,比如学生坐在一起的情况 ['1', '1']
,或者中间只隔着一个座位的情况 ['1', '0', '1']
。其他所有符合正确数量的 '1'
和 '0'
的安排都是合法的,这里有个例子:
def isvalid(a, n, k):
if not isinstance(a, basestring):
a = ''.join(a) # `a` is a list of '1', '0'
return (len(a) == n and a.count('1') == k and a.count('0') == (n-k) and
all(p not in a for p in ['11', '101']))
还有一些更高效的算法可以生成合法的子集,而不需要检查所有的子集,比如:
def subsets(n, k):
assert k >= 0 and n >= 0
if k == 0: # no students, all seats are empty
yield '0'*n
elif k == 1 and (n == 1 or n == 2): # the last student at the end of the row
yield '1' + '0'*(n-1) # either '1' or '10'
if n == 2: yield '01'
elif n > 3*(k-1): # there are enough empty seats left for k students
for s in subsets(n-3, k-1):
yield '100' + s # place a student
for s in subsets(n-1, k):
yield '0' + s # add empty seat
例子
n, k = 5, 2
for s in subsets(n, k):
assert isvalid(s, n, k)
print(s)
输出
10010
10001
01001