我正在使用这个算法来划分(1<<N)-1,给定N.这是代码:

void btrack(int k, int N, int sum, int used, int rep) {
    //if (k > (N + 1)) exit(-1);
    //printf("called with k = %d, N = %d, sum = %d, used = %d, rep = %d\n", k, N, sum, used, rep);
    
    if (sum == (1 << N) - 1) {
        printf("ended with %d \n", rep);
        return;
    }
    if ((sum > (1 << N) - 1) || (used & 1 << k))
        return;
    used |= (1 << k);

    for (int i = 1; i <= (1 << N) - 1; i++) {
        if (!(used & 1 << i))
            btrack(i, N, sum + i, used, rep * 10 + i);
    }
}

我不想要规则的分区.分区中的数字必须是唯一的. 快速示例:

N = 3 -> (1<<N)-1 = 7
the Valid answers are :
1 + 2 + 4 = 7
2 + 5 = 7
6 + 1 = 7
4 + 3 = 7
7 = 7

the likes of :
2 + 2 + 2 + 1 = 7
1 + 1 + 1 + 1 + 3 = 7
etc...
are all incorrect because some numbers are repeated

我以为我实现了这一逻辑,但结果令人失望.有时我会得到比目标更大的金额,并不断重复.

rep变量将保存用于总和的数字: 1+2+31232+525

谢谢

推荐答案

您错误地使用了变量k.它似乎保存了您try 测试的最后一个值.递归的本质是您试图将问题分解为较小的问题,因此如果您想避免重复,则永远不应该在递归调用中使用相同的值.

取而代之的是从k+1开始循环:

#include <stdio.h>

void btrack(int k, int N, int sum, int used, int rep) {
    if (sum == (1 << N) - 1) {
        printf("ended with %d \n", rep);
        return;
    }
    if ((sum > (1 << N) - 1) || (used & 1 << k))
        return;
    used |= (1 << k);

    for (int i = k+1; i <= (1 << N) - 1; i++) {
        btrack(i, N, sum + i, used, rep * 10 + i);
    }
}

int main() {
    btrack(0, 3, 0, 0, 0);
}

输出:

ended with 124 
ended with 16 
ended with 25 
ended with 34 
ended with 7 

现在,这一切工作,直到你需要分区非常大的值.你的基数为10的rep值几乎立即变得无用,而你的used值则用完了位.当你想真正得到一个结果时,你需要搜索一个主要由零组成的位array.

另一种解决方案是将路径存储在数组中.它可能看起来像这样(半健壮但有点天真):

int run_foo(int value, int sum_parts[], int max_count, int count)
{
    if (count + 1 >= max_count)
        return 0;

    // Loop over all potential next values
    for (int next = sum_parts[count] + 1; next <= value / 2; next++) {
        sum_parts[count + 1] = next;
        if (!run_foo(value - next, sum_parts, max_count, count + 1))
            return 0;
    }

    // Print result
    if (sum_parts[count] < value) {
        for (int i = 1; i <= count; i++) {
            printf("%d + ", sum_parts[i]);
        }
        printf("%d\n", value);
    }
    return 1;
}

int foo(int value, int sum_parts[], int max_count)
{
    if (max_count < 1)
        return 0;
    sum_parts[0] = 0;
    return run_foo(value, sum_parts, max_count, 0);
}

int main()
{
    const int max_count = 10;
    int sum_parts[max_count];
    foo(15, sum_parts, max_count);
}

输出:

1 + 2 + 3 + 4 + 5
1 + 2 + 3 + 9
1 + 2 + 4 + 8
1 + 2 + 5 + 7
1 + 2 + 12
1 + 3 + 4 + 7
1 + 3 + 5 + 6
1 + 3 + 11
1 + 4 + 10
1 + 5 + 9
1 + 6 + 8
1 + 14
2 + 3 + 4 + 6
2 + 3 + 10
2 + 4 + 9
2 + 5 + 8
2 + 6 + 7
2 + 13
3 + 4 + 8
3 + 5 + 7
3 + 12
4 + 5 + 6
4 + 11
5 + 10
6 + 9
7 + 8
15

在这里,使用一个数组来存储当前值的路径,递归调用试图通过只使用大于上次使用的值的值来从剩余的值中构建一个值.这会使用更多的内存,但性能的伸缩性应该会更好.

请注意,由于数组本质上是一个堆栈,因此您可能根本不需要递归就可以实现此解决方案,只需额外小心一点.这是满分"Exercise for the Reader"分.

C++相关问答推荐

设计处理各种数据类型的方法和数据 struct

Malloc(sizeof(char[Length]))是否不正确?

如何将长字符串转换为较小的缩写,该缩写由第一个字符、最后一个字符和中间的字符数组成?

文件权限为0666,但即使以超级用户身份也无法打开

在CLANG中调试预处理器宏

`#if`条件中是否允许`sizeof`?

实现简单字典时C语言中的段错误

如何在提取的索引中分配空值?

使用TCL C API导航到列表中的元素

当b是无符号字符时,int a=(b<;<;2)>;>;2;和int a=b&;0x3F;之间有什么区别?

试图创建一个基本的Word克隆,但遇到了障碍

unions 的原子成员是个好主意吗?

赋值两侧的后置增量,字符指针

将复合文字数组用作临时字符串缓冲区是否合理?

使用 GCC 将一个函数中初始化的 struct 体实例通过指针传递到 C 中的另一个函数会产生不同的结果

(GNU+Linux) 多个线程同时调用malloc()

可以从指针数组中的值初始化指针吗?

获取 struct 中匿名 struct 的大小

在 C23 之前如何对空指针使用nullptr?

如何在 C 中编辑 struct 体中的多个变量