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

时间:2014-03-21 14:49:30

标签: algorithm dynamic-programming

如果我们有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步将是正确的基础案例?

13 个答案:

答案 0 :(得分: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等。

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

答案 1 :(得分:11)

Python解决方案:

递归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)

递归O(log(n))

这是对这个答案的评论。这个tribonacci - 倍增解决方案类似于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.

迭代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

迭代O(log(n))(从左到右)

这是对这个答案的评论。这种修改的迭代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),则序列前进一次迭代。在对有效整数求幂问题理解相同之后,这直观有意义。

迭代O(log(n))(从右到左)

这是对这个答案的评论。 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

逼近

近似当然主要用于非常大的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,之后它不同。

答案 2 :(得分:3)

这是我在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)}"}


和更短的版本:

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)}"}


一旦你知道它与斐波纳契数列相似,你就不会使用递归,而是使用迭代方法:

# 
# 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)}"}


输出:

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,那么它涉及手动计数。

答案 3 :(得分:2)

轻松获得问题的直觉:

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

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

如何?

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

  1. 要么你在步骤3并采取一步
  2. 或者你在第2步并采取两步跳跃
  3. 这两个是您进入第4步的唯一可能性

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

    1. 要么你在步骤1中又迈出了一步
    2. 或者你在步骤0并采取两步跳跃
    3.   

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

      F(0)= 0且F(1)= 1是基本情况。从这里开始建造F(2),F(3)等等。这类似于 Fibonacci系列。

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

      因此公式将成为

        

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

      观看此视频,了解Staircase Problem Fibonacci Series

      轻松理解代码:geeksforgeeks staircase problem

答案 4 :(得分:1)

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

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

// 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;
    }
}

答案 5 :(得分: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]

答案 6 :(得分:0)

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

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

第一步[] - &gt; [[1],[2],[3]]
第二步[[1],[2],[3]] - &gt; [[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 五 8 13 21

我的步骤功能称为构建

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;
}

}

答案 7 :(得分:0)

这是使用动态编程的 O N k )Java实现:

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

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

#banner {
    background-image: url(../images/2000%20x%20150_Bubble_Final_Crop.jpg);
    height: 150px;
}
body {
    width: 1200px;
}
h1 {
    color: #FFFFFF;
    padding-top: 25px;
    font-family: Cambria, "Hoefler Text", "Liberation Serif", 
    Times, "Times New Roman", serif;
    font-size: 58pt;
    text-shadow: 2px 2px 24px #D7DC23;
    text-align: center;
}
.container-fluid {
    margin: 0 auto;

}

答案 8 :(得分:0)

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

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

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

List<EffectList> contactList;
Context context;
private LayoutInflater mInflater;


// Constructors
public MyContactAdapter1(Context context, List<EffectList> objects) {
    super(context, 0, objects);
    this.context = context;
    this.mInflater = LayoutInflater.from(context);
    contactList = objects;
}

@Override
public EffectList getItem(int position) {
    return contactList.get(position);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final MyContactAdapter1.ViewHolder1 vh;
    if (convertView == null) {
        View view = mInflater.inflate(R.layout.get_layout_row_view, parent, false);
        vh = MyContactAdapter1.ViewHolder1.create((RelativeLayout) view);
        view.setTag(vh);
    } else {
        vh = (MyContactAdapter1.ViewHolder1) convertView.getTag();
    }

    EffectList item = getItem(position);



    //   vh.textViewName.setText(item.getEffectsId());

    vh.textViewName.setText(item.getEffectsName());
    vh.textViewEmail.setText(item.getEffectsId());
    // Picasso.with(context).load(item.getProfilePic()).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).into(vh.imageView);

    return vh.rootView;
}

private static class ViewHolder1 {
    public final RelativeLayout rootView;
    public final ImageView imageView;
    public final TextView textViewName;
    public final TextView textViewEmail;

    private ViewHolder1(RelativeLayout rootView, ImageView imageView, TextView textViewName, TextView textViewEmail) {
        this.rootView = rootView;
        this.imageView = imageView;
        this.textViewName = textViewName;
        this.textViewEmail = textViewEmail;
    }

    public static MyContactAdapter1.ViewHolder1 create(RelativeLayout rootView) {
        ImageView imageView = (ImageView) rootView.findViewById(R.id.imageView);
        TextView textViewName = (TextView) rootView.findViewById(R.id.textViewName);
        TextView textViewEmail = (TextView) rootView.findViewById(R.id.textViewEmail);
        return new MyContactAdapter1.ViewHolder1(rootView, imageView, textViewName, textViewEmail);
    }
}

我的博客文章:

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

答案 9 :(得分: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);
}

答案 10 :(得分: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

答案 11 :(得分: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));
    }
}

答案 12 :(得分:-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,

我猜这是模式