我编写了一个程序,解决了24(link的广义版本。也就是说,给定一组n
个数字,是否有办法对它们执行二进制运算,以便它们计算到目标数。
为此,我将可能的表达式视为由'v'
或'o'
组成的字符数组,其中'v'
是值的占位符,'o'
是占位符的操作。请注意,如果有n
个值,则必须进行n-1
次操作。
程序当前的工作原理是它按字典顺序检查{'o','o',...,'v',v'...}
的每个排列,并查看前缀表达式是否有效。例如,n = 4
时,以下表达式被视为有效:
{‘o’,’o’,’o’,’v’,’v’,’v’,’v’}
{‘o’, ‘v’, ‘o’, ‘v’, ‘o’, ‘v’, ‘v’}
以下表达式无效:
{‘v’,’o’,’v’,’o’,’o’,’v’,’v’}
{‘o’,’o’,’v’,’v’,’v’,’o’,’v’}
我的问题是,是否存在一种有效的算法来获得在某种排序中有效的下一个排列?目标是消除必须检查表达式是否对每个排列都有效。
此外,如果存在这样的算法,是否存在O(1)
时间来计算k
有效排列?
我假设长度A
的前缀表达式2n-1
被认为是有效的,当且仅当
number of operations < number of values
A[i:2n-1)
其中0<=i<2n-1
(从i
开始到2n-1
结束(不包括)的子阵列
此外,这意味着在(1/n)C(2n-2,n-1)
为C(n,k)
的情况下,确实存在n choose k
个有效排列。
答案 0 :(得分:2)
以下是如何生成ov
- 模式。下面代码背后的细节是在Knuth Volume 4A中(或至少提到过;我可能已经完成了其中一个练习)。在更改模式之前,您可以使用现有的置换机制来对每个方向的值进行置换。
代码
#include <cstdio>
namespace {
void FirstTree(int f[], int n) {
for (int i = n; i >= 0; i--) f[i] = 2 * i + 1;
}
bool NextTree(int f[], int n) {
int i = 0;
while (f[i] + 1 == f[i + 1]) i++;
f[i]++;
FirstTree(f, i - 1);
return i + 1 < n;
}
void PrintTree(int f[], int n) {
int i = 0;
for (int j = 0; j < 2 * n; j++) {
if (j == f[i]) {
std::putchar('v');
i++;
} else {
std::putchar('o');
}
}
std::putchar('v');
std::putchar('\n');
}
}
int main() {
constexpr int kN = 4;
int f[1 + kN];
FirstTree(f, kN);
do {
PrintTree(f, kN);
} while (NextTree(f, kN));
}
生成输出
ovovovovv
oovvovovv
ovoovvovv
oovovvovv
ooovvvovv
ovovoovvv
oovvoovvv
ovoovovvv
oovovovvv
ooovvovvv
ovooovvvv
oovoovvvv
ooovovvvv
oooovvvvv
有一种方法可以获得第k个树,但是在时间O(n)而不是O(1)。神奇的单词是unranking binary trees。