繁体   English   中英

给定整数n,返回它可以表示为1和2之和的方式的数量

[英]Given an integer n, return the number of ways it can be represented as a sum of 1s and 2s

例如:

5 = 1+1+1+1+1

5 = 1+1+1+2

5 = 1+1+2+1

5 = 1+2+1+1

5 = 2+1+1+1


5 = 1+2+2

5 = 2+2+1

5 = 2+1+2

任何人都可以提供关于如何做到这一点的伪代码的提示。 老实说,不知道如何开始。 这看起来像指数问题可以在线性时间内完成吗?

谢谢。

在示例中,您提供的加数顺序很重要。 (请参阅示例中的最后两行)。 考虑到这一点,答案似乎与斐波那契数字有关。 F(n)n可以写为1和2的方式。 然后最后加入的是1或2.所以F(n) = F(n-1) + F(n-2) 这些是初始值:

F(1) = 1 (1 = 1)
F(2) = 2 (2 = 1 + 1, 2 = 2)

这实际上是第(n + 1)个斐波纳契数。 原因如下:

让我们称f(n)表示n的方式的数量。 如果你有n,那么你可以把它表示为(n-1)+1或(n-2)+2。 因此,表示它的方式是表示它的方式的数量是f(n-1)+ f(n-2)。 这与Fibonacci数相同。 此外,我们看到n = 1然后我们有1路,如果n = 2那么我们有2路。 因此第(n + 1)个斐波那契数是你的答案。 有很多算法可以非常快速地计算出巨大的斐波纳契数。

排列

如果我们想要知道有多少可能的排序,那么在一些大小为n的情况下没有重复(即,从可用池中删除所选择的元素), n (或n! )的阶乘给出了答案:

double factorial(int n)
{
    if (n <= 0)
        return 1;
    else
        return n * factorial(n - 1);
}

注意:这也有一个迭代解决方案,甚至可以使用gamma函数进行近似:

std::round(std::tgamma(n + 1)); // where n >= 0

问题集以全1开始。 每次设置更改时,两个1将替换为一个2 我们想要找到k个项目( 2s )可以在一组大小n中排列的方式的数量。 我们可以通过计算来查询可能的排列数:

double permutation(int n, int k)
{
    return factorial(n) / factorial(n - k);
}

但是,这不是我们想要的结果。 问题是,排列考虑排序,例如,序列2,2,2将计为六个不同的变化。

组合

这些基本上是忽略排序的排列。 由于订单不再重要,许多排列都是多余的。 通过计算k可以找到每个排列的冗余 将排列数除以此值可得出组合数:

注意:这称为二项式系数 ,应理解为“ n选择k”

double combination(int n, int k)
{
    return permutation(n, k) / factorial(k);
}

int solve(int n)
{
    double result = 0;

    if (n > 0) {
        for ( int k = 0; k <= n; k += 1, n -= 1 )
            result += combination(n, k);
    }
    return std::round(result);
}

这是一般解决方案。 例如,如果问题是找到整数可以表示为13之和的方式的数量,我们只需要在每次迭代时调整集合大小( n - 2 )的减量。

斐波纳契数

使用Fibonacci数的解决方案起作用的原因与它们与二项式系数的关系有关。 二项式系数可以被安排成形成Pascal三角形 ,当存储为下三角矩阵时,可以使用nk作为行/列索引来访问该二进制系数,以将该元素定位为等于组合(n,k)

nk的模式随着它们在求解的生命周期中的变化而变化,当在2-D网格上作为坐标观察时绘制对角线。 沿Pascal三角形的对角线求和值的结果是斐波那契数。 如果模式改变(例如,当找到13的总和时),则不再是这种情况,并且该解决方案将失败。

有趣的是,Fibonacci数可以在恒定时间内计算。 这意味着我们只需找到第(n + 1 )个 Fibonacci数就可以在恒定时间内解决这个问题。

int fibonacci(int n)
{
    constexpr double SQRT_5 = std::sqrt(5.0);
    constexpr double GOLDEN_RATIO = (SQRT_5 + 1.0) / 2.0;

    return std::round(std::pow(GOLDEN_RATIO, n) / SQRT_5);
}

int solve(int n)
{
    if (n > 0)
        return fibonacci(n + 1);
    return 0;
}

最后,由factorialfibonacci函数生成的数字可能非常大。 因此,如果n很大,则可能需要大型数学库。

这是使用回溯的代码,它可以解决您的问题。 在每一步,同时记住到目前为止用于获得总和的数字(使用此处的向量),首先制作它们的副本,首先从n中减去1并将其添加到副本然后用n-1和副本重复向量添加1并在n == 0时打印。 然后返回并重复2,这基本上是回溯。

#include <stdio.h>
#include <vector>
#include <iostream>
using namespace std;
int n;
void print(vector<int> vect){
    cout << n <<" = ";
    for(int i=0;i<vect.size(); ++i){
       if(i>0)
           cout <<"+" <<vect[i];
       else cout << vect[i];
    }
    cout << endl;
}

void gen(int n, vector<int> vect){
   if(!n)
      print(vect);
   else{
      for(int i=1;i<=2;++i){
          if(n-i>=0){
              std::vector<int> vect2(vect);
              vect2.push_back(i);
              gen(n-i,vect2);
          }
      }
   }
}

int main(){
   scanf("%d",&n);
   vector<int> vect;
   gen(n,vect);
}

这个问题可以很容易地显示如下:

考虑一只青蛙,它出现在楼梯前面。 它需要到达第n-th楼梯,但他一次只能在楼梯上跳1或2步。 找出他可以到达第n-th楼梯的方式?

T(n)表示到达第n-th楼梯的方式的数量。

所以, T(1) = 1T(2) = 2 (2个一步跳跃或1个两步跳跃,所以2种方式)

为了到达第n-th楼梯,我们已经知道到达第(n-1)th楼梯和第(n-2)th楼梯的方式的数量。

因此,一旦从第(n-1)th阶梯跳过​​一步或从第(n-2)th阶段跳出两步就可以简单地到达第n-th阶梯......

因此, T(n) = T(n-1) + T(n-2)

希望能帮助到你!!!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM