n步骤采取1,2或3步骤。有多少种方法可以达到顶峰?

问题描述 投票:15回答:13

如果我们有n个步骤并且我们一次可以上升1步或2步,则步数和爬升方式之间存在斐波纳契关系。 IF和ONLY,如果我们不计算2 + 1和1 + 2不同。

但是,这不再是这种情况,并且必须添加我们添加第三个选项,采取3个步骤。我该怎么做呢?

是)我有的:

1 step = 1 way
2 steps = 2 ways: 1+1, 2
3 steps = 4 ways: 1+1+1, 2+1, 1+2, 3

我不知道从哪里可以找到n楼梯的路数

我得到7为n = 4和14得到n = 5我通过做它之前的所有组合的总和得到14 + 7 + 4 + 2 + 1。所以n步的方式= n-1种方式+ n-2种方式+ .... 1种方式假设我保留了所有的值。 DYNAMIC编程。 1 2和3步将是基本情况是正确的?

algorithm dynamic-programming
13个回答
33
投票

我会说公式将以下列方式看待:

K(1) = 1
K(2) = 2
k(3) = 4
K(n) = K(n-3) + K(n-2) + K(n - 1)

公式说,为了达到第n步,我们必须首先达到:

  • 第n-3步然后立即采取3步,即K(n-3)
  • 或第n-2步,然后一次采取2步,即K(n-2)
  • 或第n-1步然后立即采取1步,即K(n-1)

K(4)= 7,K(5)= 13等

您可以使用递归公式或使用动态编程。


0
投票

这是一个使用动态编程的O(Nk)Java实现:

public class Sample {
    public static void main(String[] args) {
        System.out.println(combos(new int[]{4,3,2,1}, 100));
    }

    public static int combos(int[] steps, int stairs) {
        int[][] table = new int[stairs+1][steps.length];
        for (int i = 0; i < steps.length; i++) {

            for (int n = 1; n <= stairs; n++ ) {
                int count = 0;
                if (n % steps[i] == 0){
                    if (i == 0)
                        count++;
                    else {
                        if (n <= steps[i])
                            count++;
                    }
                }

                if (i > 0 && n > steps[i]) {
                    count += table[n - steps[i]][i];
                }

                if (i > 0)
                    count += table[n][i-1];

                table[n][i] = count;
            }
        }

        for (int n = 1; n < stairs; n++) {
            System.out.print(n + "\t");
            for (int i = 0; i < steps.length; i++) {
                System.out.print(table[n][i] + "\t");
            }
            System.out.println();
        }
        return table[stairs][steps.length-1];
    }
}

我的想法是从左到右一次填写下表1列:

N    (4)    (4,3)  (4,3,2) (4,3,2,1)
1   0   0   0   1   
2   0   0   1   2   
3   0   1   1   3   
4   1   1   2   5   
5   0   0   1   6   
6   0   1   3   9   
7   0   1   2   11  
8   1   1   4   15  
9   0   1   3   18  
10  0   1   5   23  
11  0   1   4   27  
12  1   2   7   34  
13  0   1   5   39  
..
..
99  0   9   217 7803
100             8037

0
投票

以下是使用1,2和3步骤的几种方法

1: 1
2: 11   2
3: 111 12 21 3
4: 1111 121 211 112 22 13 31
5: 11111 1112 1121 1211 2111 122 212 221 113 131 311 23 32
6: 111111 11112 11121 11211 12111 21111 1113 1131 1311 3111 123 132 312 321 213 231 33 222 1122 1221 2211 1212 2121 2112

所以根据上面的组合,soln应该是:

K(n) = K(n-3) + K(n-2) + K(n - 1)
k(6) = 24 which is k(5)+k(4)+k(3) = 13+7+4

0
投票

基于Michał's answer的Java递归实现:

public class Tribonacci {
    // k(0) = 1
    // k(1) = 1
    // k(2) = 2
    // k(3) = 4
    // ...
    // k(n) = k(n-3) + k(n-2) + k(n - 1)

    static int get(int n) {
        if (n == 0) {
            return 1;
        } if (n == 1) {
            return 1;
        } else if (n == 2) {
            return 2;
        //} else if (n == 3) {
        //    return 4;
        } else {
            return get(n - 3) + get(n - 2) + get(n - 1);
        }
    }

    public static void main(String[] args) {
        System.out.println("Tribonacci sequence");
        System.out.println(Tribonacci.get(1));
        System.out.println(Tribonacci.get(2));
        System.out.println(Tribonacci.get(3));
        System.out.println(Tribonacci.get(4));
        System.out.println(Tribonacci.get(5));
        System.out.println(Tribonacci.get(6));
    }
}

-4
投票

也许它只是2 ^(n-1),其中n是步数?

这对我来说很有意义,因为有4个步骤,你有8种可能性:

4,
3+1,
1+3,
2+2,
2+1+1,
1+2+1,
1+1+2,
1+1+1+1,

我想这就是模式


11
投票

Python解决方案:

Recursive O(n)

这是基于answer by Michael。这需要O(n)CPU和O(n)内存。

import functools

@functools.lru_cache(maxsize=None)
def recursive(n):
    if n < 4:
        initial = [1, 2, 4]
        return initial[n-1]
    else:
        return recursive(n-1) + recursive(n-2) + recursive(n-3)

Recursive O(log(n))

这是对这个答案的评论。这种tribonacci-by-doubleling解决方案类似于Nayuki在solution中通过加倍的algorithms斐波那契。请注意,乘法具有比常数更高的复杂度。这不需要缓存或从缓存中受益。

def recursive_doubling(n):
    def recursive_tribonacci_tuple(n):
        """Return the n, n+1, and n+2 tribonacci numbers for n>=0.

        Tribonacci forward doubling identities:
        T(2n)   = T(n+1)^2 + T(n)*(2*T(n+2) - 2*T(n+1) - T(n))
        T(2n+1) = T(n)^2 + T(n+1)*(2*T(n+2) - T(n+1))
        T(2n+2) = T(n+2)^2 + T(n+1)*(2*T(n) + T(n+1))
        """
        assert n >= 0
        if n == 0:
            return 0, 0, 1  # T(0), T(1), T(2)

        a, b, c = recursive_tribonacci_tuple(n // 2)
        x = b*b + a*(2*(c - b) - a)
        y = a*a + b*(2*c - b)
        z = c*c + b*(2*a + b)

        return (x, y, z) if n % 2 == 0 else (y, z, x+y+z)
    return recursive_tribonacci_tuple(n)[2]  # Is offset by 2 for the steps problem.

Iterative O(n)

这是由answer by 太極者無極而生推动的。它是iterative fibonacci solution的改进的tribonacci扩展。它是从tribonacci修改的,它返回c,而不是a

def iterative(n):
    a, b, c = 0, 0, 1
    for _ in range(n):
        a, b, c = b, c, a+b+c
    return c

Iterative O(log(n)) (left to right)

这是对这个答案的评论。这种修改的迭代tribonacci-by-doubleling解决方案来自相应的递归解决方案。有些背景,请参阅herehere。它是从tribonacci修改的,它返回c,而不是a。请注意,乘法具有比常数更高的复杂度。

n的位从左到右迭代,即MSBLSB

def iterative_doubling_l2r(n):
    """Return the n+2 tribonacci number for n>=0.

    Tribonacci forward doubling identities:
    T(2n)   = T(n+1)^2 + T(n)*(2*T(n+2) - 2*T(n+1) - T(n))
    T(2n+1) = T(n)^2 + T(n+1)*(2*T(n+2) - T(n+1))
    T(2n+2) = T(n+2)^2 + T(n+1)*(2*T(n) + T(n+1))
    """
    assert n >= 0
    a, b, c = 0, 0, 1  # T(0), T(1), T(2)
    for i in range(n.bit_length() - 1, -1, -1):  # Left (MSB) to right (LSB).
        x = b*b + a*(2*(c - b) - a)
        y = a*a + b*(2*c - b)
        z = c*c + b*(2*a + b)
        bit = (n >> i) & 1
        a, b, c = (y, z, x+y+z) if bit else (x, y, z)
    return c

笔记:

  • list(range(m - 1, -1, -1)) == list(reversed(range(m)))
  • 如果该位是奇数(1),则序列前进一次迭代。在对有效整数求幂问题理解相同之后,这直观有意义。

Iterative O(log(n)) (right to left)

这是对这个答案的评论。 n的位从右到左迭代,即LSB到MSB。这种方法可能不是规定性的。

def iterative_doubling_r2l(n):
    """Return the n+2 tribonacci number for n>=0.

    Tribonacci forward doubling identities:
    T(2n)   = T(n+1)^2 + T(n)*(2*T(n+2) - 2*T(n+1) - T(n))
    T(2n+1) = T(n)^2 + T(n+1)*(2*T(n+2) - T(n+1))
    T(2n+2) = T(n+2)^2 + T(n+1)*(2*T(n) + T(n+1))

    Given Tribonacci tuples (T(n), T(n+1), T(n+2)) and (T(k), T(k+1), T(k+2)),
    we can "add" them together to get (T(n+k), T(n+k+1), T(n+k+2)).

    Tribonacci addition formulas:
    T(n+k)   = T(n)*(T(k+2) - T(k+1) - T(k)) + T(n+1)*(T(k+1) - T(k)) + T(n+2)*T(k)
    T(n+k+1) = T(n)*T(k) + T(n+1)*(T(k+2) - T(k+1)) + T(n+2)*T(k+1)
    T(n+k+2) = T(n)*T(k+1) + T(n+1)*(T(k) + T(k+1)) + T(n+2)*T(k+2)
    When n == k, these are equivalent to the doubling formulas.
    """
    assert n >= 0
    a, b, c = 0, 0, 1  # T(0), T(1), T(2)
    d, e, f = 0, 1, 1  # T(1), T(2), T(3)
    for i in range(n.bit_length()):  # Right (LSB) to left (MSB).
        bit = (n >> i) & 1
        if bit:
            # a, b, c += d, e, f
            x = a*(f - e - d) + b*(e - d) + c*d
            y = a*d + b*(f - e) + c*e
            z = a*e + b*(d + e) + c*f
            a, b, c = x, y, z
        # d, e, f += d, e, f
        x = e*e + d*(2*(f - e) - d)
        y = d*d + e*(2*f - e)
        z = f*f + e*(2*d + e)
        d, e, f = x, y, z
    return c

Approximations

近似当然主要用于非常大的n。使用取幂操作。注意,取幂具有比常数更高的复杂性。

def approx1(n):
    a_pos = (19 + 3*(33**.5))**(1./3)
    a_neg = (19 - 3*(33**.5))**(1./3)
    b = (586 + 102*(33**.5))**(1./3)
    return round(3*b * ((1./3) * (a_pos+a_neg+1))**(n+1) / (b**2 - 2*b + 4))

上面的approximation被测试是正确的,直到n = 53,之后它不同。使用更高精度的浮点运算肯定有可能在实践中得到更好的近似。

def approx2(n):
    return round((0.618363 * 1.8392**n + \
                  (0.029252 + 0.014515j) * (-0.41964 - 0.60629j)**n + \
                  (0.029252 - 0.014515j) * (-0.41964 - 0.60629j)**n).real)

上面的approximation被测试是正确的,直到n = 11,之后它不同。


3
投票

This is my solution in Ruby:

# recursion requirement: it returns the number of way up
# a staircase of n steps, given that the number of steps
# can be 1, 2, 3

def how_many_ways(n)
  # this is a bit Zen like, if 0 steps, then there is 1 way
  # and we don't even need to specify f(1), because f(1) = summing them up
  # and so f(1) = f(0) = 1
  # Similarly, f(2) is summing them up = f(1) + f(0) = 1 + 1 = 2
  # and so we have all base cases covered
  return 1 if n == 0

  how_many_ways_total = 0
  (1..3).each do |n_steps|
    if n >= n_steps
      how_many_ways_total += how_many_ways(n - n_steps)
    end
  end
  return how_many_ways_total
end

0.upto(20) {|n| puts "how_many_ways(#{n}) => #{how_many_ways(n)}"}

and a shorter version:

def how_many_ways(n)
  # this is a bit Zen like, if 0 steps, then there is 1 way
  # if n is negative, there is no way and therefore returns 0
  return 1 if n == 0
  return 0 if n < 0
  return how_many_ways(n - 1) + how_many_ways(n - 2) + how_many_ways(n - 3)
end

0.upto(20) {|n| puts "how_many_ways(#{n}) => #{how_many_ways(n)}"}

and once you know it is similar to fibonacci series, you wouldn't use recursion, but use an iterative method:

# 
# from 0 to 27: recursive: 4.72 second
#               iterative: 0.03 second
#

def how_many_ways(n)
  arr = [0, 0, 1]
  n.times do
    new_sum = arr.inject(:+)    # sum them up
    arr.push(new_sum).shift()
  end
  return arr[-1]
end

0.upto(27) {|n| puts "how_many_ways(#{n}) => #{how_many_ways(n)}"}

output:

how_many_ways(0) => 1
how_many_ways(1) => 1
how_many_ways(2) => 2
how_many_ways(3) => 4
how_many_ways(4) => 7
how_many_ways(5) => 13
how_many_ways(6) => 24
how_many_ways(7) => 44
how_many_ways(8) => 81
how_many_ways(9) => 149
how_many_ways(10) => 274
how_many_ways(11) => 504
how_many_ways(12) => 927
how_many_ways(13) => 1705
  .
  .
how_many_ways(22) => 410744
how_many_ways(23) => 755476
how_many_ways(24) => 1389537
how_many_ways(25) => 2555757
how_many_ways(26) => 4700770
how_many_ways(27) => 8646064

我喜欢@MichałKomorowski的解释和@rici的评论。以为我认为如果它取决于知道K(3) = 4,那么它涉及手动计数。


2
投票

轻松获得问题的直觉:

认为你正在爬楼梯,你可以采取的步骤是1 & 2

总数没有。到达第4步的方法=总数没有。达到第3步的方法+达不到第2步的方法

怎么样?

基本上,只有两个可能的步骤,您可以从哪里到达第4步。

  1. 你是在第3步,迈出了一步
  2. 或者你在第2步并迈出了两步

这两个是您进入第4步的唯一可能性

同样,只有两种可能的方法来达到第2步

  1. 要么你在步骤1中又迈出了一步
  2. 或者你在第0步并迈出了两步

F(n)= F(n-1)+ F(n-2)

F(0)= 0且F(1)= 1是基本情况。从这里开始建造F(2),F(3)等等。这与斐波那契系列相似。

如果可能的步数增加,比如说[1,2,3],现在每一步都有一个选项,即你可以直接跳过三步之前

因此,公式将成为

F(n)= F(n-1)+ F(n-2)+ F(n-3)

有关了解Staircase Problem Fibonacci Series的信息,请参阅此视频

易于理解代码:geeksforgeeks staircase problem


1
投票

计算使用步骤1,2,3到达第n个楼梯的方法。

我们可以使用简单的递归方法进行计数。

// Header File
#include<stdio.h>
// Function prototype for recursive Approch
int findStep(int);
int main(){
    int n;
    int ways=0;
    ways = findStep(4);
    printf("%d\n", ways);
return 0;
}
// Function Definition
int findStep(int n){
    int t1, t2, t3;
    if(n==1 || n==0){
        return 1;
    }else if(n==2){
        return 2;
    }
    else{
        t3 = findStep(n-3);
        t2 = findStep(n-2);
        t1 = findStep(n-1);
        return t1+t2+t3;
    }
}

1
投票
def count(steps):
  sol = []
  sol.append(1)
  sol.append(1 + sol[0])
  sol.append(1 + sol[1] + sol[0])


  if(steps > 3):
    for x in range(4, steps+1):
        sol[(x-1)%3] = sum(sol)


return sol[(steps-1)%3]

0
投票

我的解决方案是在java中。我决定自下而上解决这个问题。

我从一个空数组的当前路径开始[]每一步我将添加所有可能的步长{1,2,3} 第一步[] - > [[1],[2],[3]] 第二步[[1],[2],[3]] - > [[1,1],[1,2],[1,3],[2,1],[2,2],[ 2,3],[3,1] [3,2],[3,3]]

迭代0:[] 迭代1:[[1],[2],[3]] 迭代2:[[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[3,2] ],[3,3]] 迭代3 [[1,1,1],[1,1,2],[1,1,3] ....]

序列长度如下1 2 3 5 8 13 21

我的步骤函数叫做build

public class App {



public static boolean isClimedTooHigh(List<Integer> path, int maxSteps){
    int sum = 0;
    for (Integer i : path){
        sum+=i;
    }
    return sum>=maxSteps;
}

public static void modify(Integer x){
    x++;
    return;
}

///  1    2   3

/// 11 12 13
/// 21 22 23
/// 31 32 33

///111 121
public static boolean build(List<List<Integer>> paths, List<Integer> steps, int maxSteps){
    List<List<Integer>> next = new ArrayList<List<Integer>>();
    for (List<Integer> path : paths){
        if (isClimedTooHigh(path, maxSteps)){
            next.add(path);
        }
        for (Integer step : steps){
            List<Integer> p = new ArrayList<Integer>(path);
            p.add(step);
            next.add(p);
        }
    }
    paths.clear();
    boolean completed = true;
    for (List<Integer> n : next){
        if (completed && !isClimedTooHigh(n, maxSteps))
            completed = false;
        paths.add(n);
    }

    return completed;
}

public static boolean isPathEqualToMax(List<Integer> path, int maxSteps){
    int sum = 0;
    for (Integer i : path){
        sum+=i;
    }
    return sum==maxSteps;
}

public static void calculate( int stepSize, int maxSteps ){
    List<List<Integer>> paths = new ArrayList<List<Integer>>();
    List<Integer> steps = new ArrayList<Integer>();
    for (int i =1; i < stepSize; i++){
        List<Integer> s = new ArrayList<Integer>(1);
        s.add(i);
        steps.add(i);
        paths.add(s);
    }
    while (!build(paths,steps,maxSteps));
    List<List<Integer>> finalPaths = new ArrayList<List<Integer>>();
    for (List<Integer> p : paths){
        if (isPathEqualToMax(p, maxSteps)){
            finalPaths.add(p);
        }
    }

    System.out.println(finalPaths.size());
}
public static void main(String[] args){
    calculate(3,1);
    calculate(3,2);
    calculate(3,3);
    calculate(3,4);
    calculate(3,5);
    calculate(3,6);
    calculate(3,7);

    return;
}

}


0
投票

通过1,2和3步计算覆盖距离的总方式。

递归解时间复杂度是指数的,即O(3n)。

由于再次解决了相同的子问题,因此该问题具有重叠的子问题属性。因此,最小平方和问题具有动态规划问题的两个属性。

public class MaxStepsCount {

     /** Dynamic Programming. */
     private static int getMaxWaysDP(int distance) {

           int[] count = new int[distance+1];
           count[0] = 1;
           count[1] = 1;
           count[2] = 2;

           /** Memorize the Sub-problem in bottom up manner*/
           for (int i=3; i<=distance; i++) {
                count[i] = count[i-1] + count[i-2] + count[i-3];               
           }
           return count[distance];
     }


     /** Recursion Approach. */
     private static int getMaxWaysRecur(int distance) {
           if(distance<0) {
                return 0;
           } else if(distance==0) {
                return 1;
           }
           return getMaxWaysRecur(distance-1)+getMaxWaysRecur(distance-2)
                     +getMaxWaysRecur(distance-3);
     }

     public static void main(String[] args) {
           // Steps pf 1, 2 and 3.
           int distance = 10;

           /** Recursion Approach. */
           int ways = getMaxWaysRecur(distance);
           System.out.println(ways);

           /** Dynamic Programming. */
           ways = getMaxWaysDP(distance);
           System.out.println(ways);
     }
}

我的博客文章:

http://javaexplorer03.blogspot.in/2016/10/count-number-of-ways-to-cover-distance.html


0
投票

基于递归memoization的C ++解决方案:你问一个楼梯我们有多少方法可以达到顶峰?如果它不是最顶层的楼梯,它会询问所有邻居并总结并返回结果。如果它是最顶层的楼梯,它会说1。

vector<int> getAllStairsFromHere(vector<int>& numSteps,  int& numStairs, int currentStair)
{
    vector<int> res;

    for(auto it : numSteps)
        if(it + currentStair <= numStairs)
            res.push_back(it + currentStair);

    return res;
}


int numWaysToClimbUtil(vector<int>& numSteps, int& numStairs, int currentStair, map<int,int>& memT)
{
    auto it = memT.find(currentStair);
    if(it != memT.end())
        return it->second;

    if(currentStair >= numStairs)
        return 1;

    int numWaysToClimb = 0;
    auto choices = getAllStairsFromHere(numSteps,  numStairs, currentStair);
    for(auto it : choices)
        numWaysToClimb += numWaysToClimbUtil(numSteps, numStairs, it, memT);


    memT.insert(make_pair(currentStair, numWaysToClimb));
    return memT[currentStair];
}


int numWaysToClimb(vector<int>numSteps, int numStairs)
{
    map<int,int> memT;
    int currentStair = 0;
    return numWaysToClimbUtil(numSteps, numStairs, currentStair, memT);
}
© www.soinside.com 2019 - 2024. All rights reserved.