找到从一对数字中输入数字的最小步骤

时间:2012-02-27 06:40:26

标签: algorithm math

我们假设我们有一对数字(a,b)。我们可以在一步中从给定对中获得一对新的(a + b,b)或(a,a + b)。

让初始数字对为(1,1)。我们的任务是找到数字k,即,将(1,1)转换为至少一个数等于n的对中所需的最少步骤数。 我通过找到所有可能的对来解决它,然后返回给定数字形成的最小步骤,但是它需要相当长的时间来计算。我猜这必须以某种方式与查找gcd.can有关,请帮助或提供给我这个概念的一些链接。 这是解决这个问题的程序,但对我来说并不聪明......

#include <iostream>
using namespace std;
#define INF 1000000000

int n,r=INF;

int f(int a,int b){

  if(b<=0)return INF;
  if(a>1&&b==1)return a-1;
  return f(b,a-a/b*b)+a/b;

}


int main(){

  cin>>n;
  for(int i=1;i<=n/2;i++){
    r=min(r,f(n,i));
  }
  cout<<(n==1?0:r)<<endl;

}

3 个答案:

答案 0 :(得分:1)

我解决这些问题的方法(我从projecteuler.net得到的)是计算序列的前几个项,然后在oeis中搜索具有相同项的序列。这可以导致解决方案的数量级更快。在你的情况下,序列可能是:http://oeis.org/A178031但不幸的是它没有易于使用的公式。 : 由于n的约束相对较小,因此可以从(1,1)到达对(a,b)所需的最小步数执行dp。你得到一个二维数组,存储给定对的答案,然后你做一个带有memoization的递归:

int mem[5001][5001];
int solve(int a, int b) {
  if (a == 0) {
     return mem[a][b] = b + 1;
  }
  if (mem[a][b] != -1) {
    return mem[a][b];
  }
  if (a == 1 && b == 1) {
    return mem[a][b] = 0;
  }
  int res;
  if (a > b) {
    swap(a,b);
  }
  if (mem[a][b%a] == -1) { // not yet calculated
    res = solve(a, b%a);
  } else { // already calculated
    res = mem[a][b%a];
  }
  res += b/a;
  return mem[a][b] = res;
}


int main() {
  memset(mem, -1, sizeof(mem));
  int n;
  cin >> n;
  int best = -1;
  for (int i = 1; i <= n; ++i) {
    int temp = solve(n, i);
    if (best == -1 || temp < best) {
      best = temp;
    }
  }
  cout << best << endl;
}

事实上在这种情况下,dp和BFS之间没有太大差异,但这是解决此类问题的一般方法。希望这会有所帮助。

编辑:如果a为零,则在dp中返回足够大的值

答案 1 :(得分:0)

您可以使用广度优先搜索算法来执行此操作。在每一步中,您都会生成以前未见过的所有可能的NEXT步骤。如果接下来的步骤包含结果,那么如果不重复则完成。重复此操作的次数是最小转换次数。

答案 2 :(得分:0)

首先,k-3步后你可以得到的最大数是kth fibinocci数。设t为魔术比率。

现在,n开始于(n,upper(n / t))。

If x>y:
    NumSteps(x,y) = NumSteps(x-y,y)+1
Else:
    NumSteps(x,y) = NumSteps(x,y-x)+1

迭代计算NumSteps(n,upper(n / t))

PS:使用upper(n / t)可能并不总能提供最佳解决方案。您可以围绕此值进行一些本地搜索,以获得最佳结果。为了确保最优性,您可以尝试从0到n-1的所有值,其中最坏情况复杂度为O(n ^ 2)。但是,如果最佳值来自接近上限(n / t)的值,则解为O(nlogn)