回溯算法的复杂性

时间:2017-06-14 14:14:21

标签: algorithm backtracking

我尝试使用回溯来解决这个问题,但我不确定算法的复杂性(如果算法是正确的)以及具有更好复杂性的算法。

给定2个正整数n和m,如果符合以下条件,我们称合法为整数序列:

  • 序列的长度为n
  • 序列中的元素介于1和m之间
  • 序列的位置i中的元素,1< i< = n,是位置i-1
  • 中元素的除数

计算合法序列的数量。算法的预期复杂度为O(m²+ nm)

这是我在c:

中的算法
// n length of the sequence
// m maximum valid number
// l number of remaining positions in the sequence
// p previous number in the sequence

int legal(int n, int m, int l, int p) {
    if (l == 0) 
        return 1;
    int q=0;
    for (int i=1; i <= m;i++) {
        if (p%i == 0 || l == n)
            q += legal(n,m,l-1,i);
    }
    return q;
}

int main() {
    int n, m;
    scanf("%d", &n);
    scanf("%d", &m);    
    printf("%d\n", legal(n,m,n,0));
}

我认为算法的复杂性是O(nmS(n)),S(n)=合法序列的数量

1 个答案:

答案 0 :(得分:0)

您的程序在问题的解决方案空间中运行是正确的。对于此类问题,您的解决方案对于大输入(例如n = m = 100)来说是次优的。这是因为解空间相对于mn呈指数增长。这是一个使用memoization来避免重新计算的解决方案:

#include <cstdio>

#define LIMIT 101
#define DIRTY -1


long long cache[LIMIT][LIMIT];

void clear_cache() {
  for (int i = 0; i < LIMIT; i++) {
    for (int j = 0; j < LIMIT; j++) {
      // marked all entries in cache as dirty
      cache[i][j] = DIRTY;
    }
  }
}

long long legal_seqs(int curr_len, int prev_num, int seq_len, int max_num) {
  // base case
  if (curr_len == seq_len) return 1;

  // if we haven't seen this sub-problem, compute it!
  // this is called memoization
  if (cache[curr_len][prev_num] == DIRTY) {
    long long ways = 0;
    // get all multiples of prev_num
    for (int next_num = 1; next_num <= max_num; next_num++) {
      if (prev_num % next_num == 0) {
        ways += legal_seqs(curr_len + 1, next_num, seq_len, max_num);
      }
    }
    cache[curr_len][prev_num] = ways;
  }
  return cache[curr_len][prev_num];
}

int main() {
  int n, m;
  scanf("%d%d", &n, &m);
  clear_cache();
  printf("%lld\n", legal_seqs(0, 0, n, m));
}

上面的代码以你提到的时间复杂度运行。