这个脑筋急转弯的易于实施的解决方案?

时间:2015-05-13 18:29:22

标签: algorithm

所以我有一个脑筋急转弯,我读过我们在我们的大学上的算法和益智聚会之一,就像这样:

  

有一所学校奖励学生在特定时期内   永远不会迟到一次,谁也不会缺席   连续三天或更多天。重复多少可能的排列   我们可以在给定的时间范围内建立(或缺乏)   给学生一个奖励?假设每一天都只是一个州   准时,迟到或缺席一整天,不要担心具体   类。示例:对于三天的时间范围,我们可以创建19个这样的时间范围   重复授予奖励的排列。

我已经将它发布在math.SE昨天因为我感兴趣,如果有一些现成的烘焙公式我们可以得出来解决它,但事实证明没有,并且所有的变换确实是相当复杂。

因此,我在这里问 - 你怎么用算法解决这个问题?我试图缩小可能性空间,但过了一段时间,重复进行所有可能的排列变得太多了,算法开始变得非常复杂,而我相信应该有一些易于实现的方法来解决它,特别是因为大多数谜题我们在聚会上的交流就像那样。

3 个答案:

答案 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