计算排列的逆下降

时间:2014-08-12 21:30:15

标签: c++ algorithm permutation

n的排列是一个长度为A的数组n,每次包含条目1,2,...,n

排列A的反向下降集是长0-1的{​​{1}}数组Dn-1如果D[i] = 0i+1 i+2左侧A以及D[i] = 1

示例(n=4):

[1, 2, 3, 4] [0, 0, 0]
[1, 2, 4, 3] [0, 0, 1]
[1, 3, 4, 2] [0, 1, 0]
[2, 3, 4, 1] [1, 0, 0]
[1, 3, 2, 4] [0, 1, 0]
[2, 3, 1, 4] [1, 0, 0]
[1, 4, 2, 3] [0, 0, 1]
[1, 4, 3, 2] [0, 1, 1]
[2, 4, 3, 1] [1, 0, 1]
[3, 4, 2, 1] [1, 1, 0]
[2, 1, 3, 4] [1, 0, 0]
[3, 1, 2, 4] [0, 1, 0]
[4, 1, 2, 3] [0, 0, 1]
[2, 1, 4, 3] [1, 0, 1]
[3, 1, 4, 2] [0, 1, 0]
[2, 4, 1, 3] [1, 0, 1]
[3, 4, 1, 2] [0, 1, 0]
[3, 2, 1, 4] [1, 1, 0]
[4, 2, 1, 3] [1, 0, 1]
[4, 3, 1, 2] [0, 1, 1]
[3, 2, 4, 1] [1, 1, 0]
[4, 2, 3, 1] [1, 0, 1]
[4, 1, 3, 2] [0, 1, 1]
[4, 3, 2, 1] [1, 1, 1]

计算置换的逆下降集的天真方法是O(n^2)。我真的更喜欢更快的东西。这是天真的事情

for (int i=0; i<n-1; ++i) {
  for (int j=i+1; j<n; ++j) {
    if (A[j] == i+2) {
      D[i] = 1;
      break;
    } else if (A[j] = i+1) {
      D[i] = 0;
      break;
    }
  }
}

这被称为反向下降,因为如果你采用排列的倒数然后采用通常的下降集,那么它就是你得到的。排列A的通常下降集是长度为D的数组n-1,其中D[i] = 1如果A[i] > A[i+1]则为0

因此,一个想法是计算置换的倒数,然后在一遍O(n)中取下降集。但是,我知道采用逆的最佳方法仍然是O(n^2),因此不能保存任何内容,但也许有更快的方法。

我用C ++写作,但任何伪代码解决方案都会很棒。

2 个答案:

答案 0 :(得分:3)

我认为你计算逆的方法很好,因为这可以在O(n)中完成。

只需将i的每个值从0循环到n-1并存储E[A[i]]=i

这会计算一个数组E,其中E[j]给出原始排列中A[i]的位置。

答案 1 :(得分:1)

这可以在O(n)中完成。

每个D[i]代表首先看到i+1i+2。因此,对于每个A[i],更新D[A[i] - 1]D[A[i] - 2](仅针对边缘情况更新其中一个),适当地设置元素。

例如[4,1,3,2]

4 => D[2] is unset so D[2] := 1
1 => D[0] is unset so D[0] := 0
3 => D[1] is unset so D[1] := 1; D[2] is already set
2 => D[1] is already set; D[0] is already set

D = [0,1,1]

代码:

//Initialize all elements of D to something other than 0 or 1; for example, 2.

for (int i=0; i<n; ++i) {

  // edge cases

  if (A[i] == 1 && D[0] == 2){
    D[0] = 0;
  } else if (A[i] == n && D[n - 2] == 2){
    D[n - 2] = 1;

  // everything else

  } else {
    if (D[ A[i] - 2 ] == 2){
      D[ A[i] - 2 ] = 1;
    }
    if (D[ A[i] - 1 ] == 2){
      D[ A[i] - 1 ] = 0;
    }
  }
}