递归树函数?

5
我是一名有用的助手,可以为您进行文本翻译。以下是需要翻译的内容:

我有一个作业卡在那里很久了。我应该考虑从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]

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

tree

#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]

结果更接近了!!


话题:这不是通常所说的树。它只是一个递归函数调用。 - Support Ukraine
我刚刚意识到我的逻辑错误 - 我在加减分支中传递了mul(没有增加),当我应该始终将它们重置为0...现在显然得多。非常感谢你!!! - Aldo
仍然有一个错误:当你递归进行加减运算时,应该传递0,但是如果你没有递归进行乘法运算,你只会将mul重置为0。这是不正确的。 - chqrlie
1
你有三个答案可供选择,可以接受其中一个答案,也可以编写自己的答案并接受它。请不要写“已解决,谢谢...” - Barmak Shemirani
@Aldo 正确的标记问题已解决的方法是接受其中一个答案。 - 2501
显示剩余5条评论
3个回答

4

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

以下是代码:

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。因此,对于以下值将采用 true 分支: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);
}

或者更好的方法是不要使用%,直接使用<
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<5)。我已经尝试了许多各种各样的起始值和检查组合,但似乎无法弄对它... - Aldo

2

我看到的主要问题是你没有正确地重置mul计数器。一旦你进行了+或-分支,你就必须将其重置,以允许5个连续的乘法。一个单独的+或-会打破这个字符串。

因此,除了4386427的答案中的重置(使用从零开始的那个;我希望你会发现它不那么混乱),你还需要:

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

这表明多路序列计数器当前为0。


@4386427 嘿,谢谢你的回复!我已经实现了正确的计数并更新了帖子。尽管更新后结果更接近,但我仍然不明白为什么出现偏差。错误一定在我的逻辑中,但我就是看不到。 - Aldo
既然我们在这个问题中帮助您解决了一些问题,我建议您在这里接受一个答案,然后使用您更新的代码和一些调试跟踪信息发布一个新问题。 - Prune

2
您的算法存在问题:
  • mul计数器应该从0开始。

  • 您应该使用if (mul < 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;
}

2
这既是个很棒的例子(尤其是打印部分),又超出了我的理解范围... 我对如何使用结构体仅有基础掌握。无论如何,我现在看到了我的错误——当递归改变运算符时,我传递了 mul 而非将其重置为 0 —— 就像你说的那样。非常感谢您... 我一定会回来学习更多的。 - Aldo
谢谢,我们期待生动活泼的评论;-) - chqrlie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接