【问题标题】:Recursive tree function?递归树函数?
【发布时间】:2017-03-13 21:18:06
【问题描述】:

我有一项任务我已经坚持了太久。我应该像这样考虑从 1 到 N 的所有可能表达式:

n = 5;

1 % 2 % 3 % 4 % 5 = ?

其中 % 可以是加法、减法或乘法(+、-、*) 我要做的是考虑这些操作的所有可能组合,并计算有多少结果表达式等于 n 本身。

因此,例如,对于 n=4,答案是 1,因为只有一个表达式等于 n。

1 + 2 - 3 + 4 = 4

对此还有一些注意事项 - 乘法的绑定比其他两个操作更强。比如

1 + 2 + 3 * 4 * 5 + 6 

需要解析为

1 + 2 + (3 * 4 * 5) + 6

此外,乘法最多只能连续使用 5 次(不是总共),因此 n=20 以下的任何内容都可以放入整数。 为了解决这个问题,我编写了这个递归树,但是在 n=15 等更高的值时,我的输出会变得不正确。

[N ] - [Expected result] [My program's result]
[5 ] - [              3] [                  3] 
[6 ] - [              1] [                  1]
[9 ] - [             27] [                 27]
[15] - [           3932] [               3911]
[16] - [           9803] [               9327]
[17] - [          23209] [              22942]

我已经尝试诊断此问题将近一周,但无法使其正常工作...我尝试使代码尽可能可读,并在必要时发表评论。只是为了解释代码的作用——它构建了一个树,其中 (+,- 和 *) 是每次迭代的分支。每个节点都是直到那个点的表达式的总和,所以当我们达到 depth = n 时,所有的结束节点都是可能的表达式总和——我们要做的就是检查它们是否等于 n。如下图:

#include <stdio.h>

int n;
int result = 0;

void tree(int depth, int sum, int mul, int last) {
    //DEPTH = recursion from 1 to n
    //SUM = the sum of the expression
    //MUL = counter to track how many consecutive multiplications have been done
    //LAST = previous number added to sum

    //if n nodes reached
    if (depth == n) {
        if (sum == n) {
            //count result
            result++;
        }
        return;  
    }
    //build tree
    depth++;
    if (mul % 5 != 0) { //if multiplication hasn't been used 5x in a row
        tree(depth, (sum - last) + (last * depth), mul + 1, last * depth);
    } else {
        //else dont build a multiplication branch, but reset the counter
        mul = 1;
    }
    //build addition and subtraction trees
    tree(depth, sum + depth, mul, depth);
    tree(depth, sum - depth, mul, depth * -1);
}

int main(int argc, char **argv) {
    scanf("%i", &n);
    tree(1, 1, 1, 1);
    printf("%i\n", result);
    return 0;
}

更新 1:MUL 计数器已更正

#include <stdio.h>

int n;
int result = 0;

void tree(int depth, int sum, int mul, int last) {
    //DEPTH = recursion from 1 to n
    //SUM = the sum of the expression
    //MUL = counter to track how many consecutive multiplications have been done
    //LAST = previous number added to sum

    //if n nodes reached
    if (depth == n) {
        if (sum == n) {
            //count result
            result++;
        }
        return;  
    }
    //build tree
    depth++;
    if (mul < 5) { //if multiplication hasn't been used 5x in a row
        tree(depth, (sum - last) + (last * depth), mul + 1, last * depth);
    } else {
        //else dont build a multiplication branch, but reset the counter
        mul = 0;
    }
    //build addition and subtraction trees
    tree(depth, sum + depth, mul, depth);
    tree(depth, sum - depth, mul, depth * -1);
}

int main(int argc, char **argv) {
    scanf("%i", &n);
    tree(1, 1, 0, 1);
    printf("%i\n", result);
    return 0;
}

更改:根据答案更正了计数器和起始值(谢谢!),但程序仍然在高值时产生不正确的结果,更新数据:

[N ] - [Expected result] [My program's result]
[5 ] - [              3] [                  3] 
[6 ] - [              1] [                  1]
[9 ] - [             27] [                 27]
[15] - [           3932] [               3924]
[16] - [           9803] [               9781]
[17] - [          23209] [              23121]

结果更接近了!!

【问题讨论】:

  • 主题:这不是通常所说的树。它只是一个递归函数调用。
  • 我刚刚意识到我的逻辑中的错误 - 我在加法和减法分支中传递了 mul (没有递增),而我应该一直将它们重置为 0 ......现在很明显。非常感谢!!!
  • 还有一个bug:你应该在递归加减法时传递0,但只有在你没有递归乘法时才将mul重置为0。这是不正确的。
  • 如果忽略超过 5 个连续乘法的组合,您将不会得到正确的结果。
  • Don't put "solvedThank you..." 你有 3 个答案,你可以接受其中一个答案,也可以写下自己的答案并接受。

标签: c recursion tree


【解决方案1】:

你的算法有问题:

  • mul 计数器应从 0 开始。

  • 您应该使用if (mul &lt; 5) 而不是if (mul % 5 != 0) 测试约束

  • 当你为不同的运算符递归时,你应该总是传递0

还请注意,建议避免使用全局变量,尤其是像 nresult 这样短而无意义的名称。最好使用向其传递指针的状态结构。

这是一个改进的版本,可以从命令行获取参数并打印解决方案:

#include <stdio.h>
#include <stdlib.h>

struct state {
    int n;
    int result;
    char ops[20];
};

void print_exp(struct state *sp, int depth, int sum) {
    for (int i = 1; i < sp->n; i++) {
        printf("%d %c ", i, sp->ops[i]);
    }
    printf("%d = %d\n", sp->n, sum);
}

void tree(struct state *sp, int depth,int sum, int mul, int last, char op) {
    // DEPTH = recursion from 1 to n
    // SUM = the sum of the expression
    // MUL = counter to track how many consecutive multiplications have been done
    // LAST = previous number added to sum

    //if n nodes reached
    sp->ops[depth - 1] = op;
    if (depth == sp->n) {
        if (sum == sp->n) {
            //count result
            sp->result++;
            print_exp(sp, depth, sum);
        }
        return;
    }
    depth++;
    if (mul < 5) { //if multiplication hasn't been used 5x in a row
        // recurse with a multiplication
        tree(sp, depth, (sum - last) + (last * depth), mul + 1, last * depth, '*');
    }
    // recurse with addition and subtraction operators
    tree(sp, depth, sum + depth, 0, depth, '+');
    tree(sp, depth, sum - depth, 0, -depth, '-');
}

int main(int argc, char **argv) {
    struct state s = { 0, 0, "" };

    if (argc > 1)
        s.n = strtol(argv[1], NULL, 0);
    else
        scanf("%i", &s.n);
    tree(&s, 1, 1, 0, 1, '\0');
    printf("%i\n", s.result);
    return 0;
}

【讨论】:

  • 这同时很棒(尤其是印刷),但远远超出了我的能力。我对如何使用结构只有一个基本的了解。无论如何,我现在看到了我的错误-正如您所说,当递归更改运算符时,我传递了 mul 而不是将其重置为 0 。非常感谢...我一定会回来的,因为我显然可以在这里学到很多东西
  • 谢谢你,我们为这样活泼的 cmets 而生 ;-)
【解决方案2】:

我不确定这是否能解决所有问题,但这是一个错误。

这段代码:

if (mul % 5 != 0) { //if multiplication hasn't been used 5x in a row
    tree(depth, (sum - last) + (last * depth), mul + 1, last * depth);
} else {
    //else dont build a multiplication branch, but reset the counter
    mul = 1;
}

错了。

首先,您从mul 为 1 开始。因此它将采用以下值的真正分支:1、2、3、4

所以你总共只能得到 4 个乘法。

试试这个:

if (mul % 6 != 0) { //if multiplication hasn't been used 5x in a row
          ^
          Notice...

    tree(depth, (sum - last) + (last * depth), mul + 1, last * depth);
}

或者更好 - 不要使用% - 只需使用&lt;

if (mul < 5) { //if multiplication hasn't been used 5x in a row
          ^
          Notice...

    tree(depth, (sum - last) + (last * depth), mul + 1, last * depth);
}

并开始使用mul等于0,即tree(1, 1, 0, 1);

【讨论】:

  • 我确实试过这个! (显然我让它处于不正确的状态)但即使进行了这种更正,产生的值也不正确(尽管更接近!),例如 n=15 产生 3924(从 mul=0 开始,检查 mul
【解决方案3】:

我看到的主要问题是您没有正确重置 mul 计数器。一旦您采用 + 或 - 分支,您必须将其重置以允许 5 连续 乘法。单个 + 或 - 会破坏该字符串。

所以,除了重置 4386427 的答案(使用从零开始的答案;我希望你会觉得它不那么混乱),你还需要

tree(depth, sum + depth, 0, depth);
tree(depth, sum - depth, 0, depth * -1);

这些识别出多序列计数器当前为 0。

【讨论】:

  • @4386427 嘿,谢谢您的回复!我实施了正确的计数并更新了帖子。我仍然不明白为什么结果会关闭(尽管它们与更新更接近)。错误一定在我的逻辑中,但我只是没有看到它。
  • 由于我们在此问题中遇到了一些问题,因此我建议您在此处接受一个答案,然后发布一个包含更新代码的新问题......以及一些调试跟踪。
猜你喜欢
  • 2012-10-29
  • 1970-01-01
  • 1970-01-01
  • 2013-03-09
  • 2018-02-06
  • 1970-01-01
  • 1970-01-01
  • 2019-09-09
相关资源
最近更新 更多