所以我有一个脑筋急转弯,我读过我们在我们的大学上的算法和益智聚会之一,就像这样:
有一所学校奖励学生在特定时期内 永远不会迟到一次,谁也不会缺席 连续三天或更多天。重复多少可能的排列 我们可以在给定的时间范围内建立(或缺乏) 给学生一个奖励?假设每一天都只是一个州 准时,迟到或缺席一整天,不要担心具体 类。示例:对于三天的时间范围,我们可以创建19个这样的时间范围 重复授予奖励的排列。
我已经将它发布在math.SE昨天因为我感兴趣,如果有一些现成的烘焙公式我们可以得出来解决它,但事实证明没有,并且所有的变换确实是相当复杂。
因此,我在这里问 - 你怎么用算法解决这个问题?我试图缩小可能性空间,但过了一段时间,重复进行所有可能的排列变得太多了,算法开始变得非常复杂,而我相信应该有一些易于实现的方法来解决它,特别是因为大多数谜题我们在聚会上的交流就像那样。
答案 0 :(得分:3)
这会尖叫递归(和/或动态编程)!
假设我们尝试解决一个稍微普遍的问题:
如果学生迟到不超过L次,我们会给予奖励,而不是 连续缺席一天或多天。
现在我们要计算n
天时间范围内的可能性数量。
调用此方法P(L, A, n)
现在尝试根据期间第一天的三个案例建立一个递归。
1)如果学生第一天准时,那么这个数字就是
P(L, A, n-1)
2)如果学生在第一天迟到,那么这个数字是
P(L-1, A, n-1)
3)如果学生第一天缺席,则该号码为
P(L, A-1, n-1)
这给了我们递归:
P(L,A,n)= P(L,A,n-1)+ P(L-1,A,n-1)+ P(L,A-1,n-1)
您可以记住递归,也可以只查找您查找的表。
注意
的基本情况 P(0, *, *), P(*, 0, *) and P(*, *, 0)
并且可以通过简单的数学公式计算。
这是一个快速的python代码,带有memoization + recursion来演示:
import math
def binom(n, r):
return math.factorial(n)/(math.factorial(r)*math.factorial(n-r))
# The memoization table.
table = {}
def P(L, A, n):
if L == 0:
# Only ontime or absent.
# More absents than period.
if A > n:
return 2**n
# 2^n total possibilities.
# of that n-A+1 are non-rewarding.
return 2**n - (n - A + 1)
if A == 0:
# Only Late or ontime.
# need fewer than L+1 late.
# This is n choose 0 + n choose 1 + ... + n choose L
total = 0
for l in xrange(0, min(L,n)):
total += binom(n, l)
return total
if n == 0:
return 1
if (L, A, n) in table:
return table[(L, A, n)]
result = P(L, A, n-1) + P(L-1, A, n-1) + P(L, A-1, n-1)
table[(L, A, n)] = result
return result
print P(1, 3, 3)
输出为19
。
答案 1 :(得分:3)
以下是@ProgrammerPerson在答案中实现递归的Python 3代码的简化版本:
from functools import lru_cache
def count_variants(max_late, base_absent, period_length):
"""
max_late – maximum allowed number of days the student can be late;
base_absent – the number of consecutive days the student can be absent;
period_length – days in a period."""
@lru_cache(max_late * base_absent * period_length)
def count(late, absent, days):
if late < 0: return 0
if absent < 0: return 0
if days == 0: return 1
return (count(late, base_absent, days-1) + # Student is on time. Absent reset.
count(late-1, base_absent, days-1) + # Student is late. Absent reset.
count(late, absent-1, days-1)) # Student is absent.
return count(max_late, base_absent, period_length)
运行示例:
In [2]: count_variants(1, 2, 3)
Out[2]: 19
答案 2 :(得分:2)
设S(n)为长度为n且不重复3次的字符串数。
任何此类字符串(长度至少为3)以“0”,“01”或“011”结尾(删除后缀后,任何不带三个连续1的字符串都会出现)。
然后对于n&gt; 2,S(n)= S(n-1)+ S(n-2)+ S(n-3),S(0)= 1,S(1)= 2,S(2)= 4。
如果你有第二天的晚些时候(从0开始计算),那么你有S(i)安排缺席前几天的方式,以及S(n-i-1)安排缺席天数的方式。
因此,对原问题的解是S(n)+和(S(i)* S(n-i-1)| i = 0 ... n-1)
我们可以像这样迭代地计算解决方案:
def ways(n):
S = [1, 2, 4] + [0] * (n-2)
for i in xrange(3, n+1):
S[i] = S[i-1] + S[i-2] + S[i-3]
return S[n] + sum(S[i] * S[n-i-1] for i in xrange(n))
for i in xrange(1, 20):
print i, ways(i)
输出:
1 3
2 8
3 19
4 43
5 94
6 200
7 418
8 861
9 1753
10 3536
11 7077
12 14071
13 27820
14 54736
15 107236
16 209305
17 407167
18 789720
19 1527607