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

时间:2016-10-15 18:35:00

标签: c++ algorithm

例如:

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

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

谢谢。

5 个答案:

答案 0 :(得分:6)

在示例中,您提供了加数顺序很重要。 (请参阅示例中的最后两行)。考虑到这一点,答案似乎与斐波那契数字有关。让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)

答案 1 :(得分: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)个斐波那契数是你的答案。有很多算法可以很快地计算出巨大的斐波纳契数。

答案 2 :(得分:1)

排列组合

如果我们想要了解一些大小 n 中有多少可能的排序而没有重复(即,从可用池中删除所选的元素),factorial的< em> 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

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

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

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

组合

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

注意:这称为binomial coefficient,应该理解为“ 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);
}

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

斐波纳契数

使用Fibonacci数的解决方案起作用的原因与它们与二项式系数的关系有关。二项式系数可以排列成Pascal's triangle,当存储为下三角矩阵时,可以使用 n k 作为行/列索引进行访问找到等于组合(n,k)的元素。

n k 的模式,因为它们在求解的生命周期内发生变化,在被视为2的坐标时绘制对角线D格。沿Pascal三角形的对角线求和值的结果是斐波那契数。如果模式发生变化(例如,当找到 1s 3s 的总和时),则不再是这种情况,此解决方案将失败。

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

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

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

答案 3 :(得分:0)

以下是使用回溯的代码,可以解决您的问题。在每一步,同时记住到目前为止用于获得总和的数字(使用此处的向量),首先制作它们的副本,首先从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);
}

答案 4 :(得分:0)

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

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

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

所以,1 = T(2)2 = n-th(2步跳跃或1步两跳,所以2种方式)

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

所以,一旦从(n-1)th楼梯跳过一步或从(n-2)th步骤跳两步就可以简单到达T(n)楼梯...

因此,T(n-1) = T(n-2) + $.getJSON('clickdates.php', function(data) { options.series[0].data = data.gauge1; var chart5 = new Highcharts.Chart(options);

希望它有所帮助!!!