【问题标题】:SUM exactly using K elements solutionSUM 完全使用 K 元素解决方案
【发布时间】:2015-08-04 07:34:41
【问题描述】:

问题:在具有 N 个数字的给定数组上,找到大小为 M(正好是 M 个元素)等于 SUM 的子集。

我正在为这个问题寻找动态规划 (DP) 解决方案。基本上是想了解矩阵填充的方法。我写了下面的程序,但没有添加记忆,因为我仍然想知道如何做到这一点。

    #include <stdio.h>
    #define SIZE(a) sizeof(a)/sizeof(a[0])
    int binary[100];
    int a[] = {1, 2, 5, 5, 100};

    void show(int* p, int size) {
            int j;
            for (j = 0; j < size; j++)
                    if (p[j])
                            printf("%d\n", a[j]);
    }

    void subset_sum(int target, int i, int sum, int *a, int size, int K) {
            if (sum == target && !K) {
                    show(binary, size);
            } else if (sum < target && i < size) {
                    binary[i] = 1;
                    foo(target, i + 1, sum + a[i], a, size, K-1);
                    binary[i] = 0;
                    foo(target, i + 1, sum, a, size, K);
            }
    }

    int main() {
            int target = 10;
            int K = 2;
            subset_sum(target, 0, 0, a, SIZE(a), K);
    }

下面的递归解决方案有意义吗?

让 DP[SUM][j][k] 求和为 SUM,其中从 0 到 j 个元素中挑选出恰好 K 个元素。

 DP[i][j][k] = DP[i][j-1][k] || DP[i-a[j]][j-1][k-1] { input array a[0....j] }

基本情况是:

DP[0][0][0] = DP[0][j][0] = DP[0][0][k] = 1
DP[i][0][0] = DP[i][j][0] = 0

这意味着我们可以考虑这个元素( DP[i-a[j]][j-1][k-1] )或者我们不考虑当前元素(DP[i][j-1][ k])。如果我们考虑当前元素,则 k 减少 1,这减少了需要考虑的元素,当不考虑当前元素时也是如此,即 K 不减少 1。

【问题讨论】:

    标签: algorithm recursion dynamic-programming recurrence


    【解决方案1】:

    您的解决方案对我来说很合适。

    现在,您基本上是在回溯所有可能性并打印每个解决方案。如果您只想要一个解决方案,您可以添加一个在找到一个解决方案时设置的标志,并在继续递归调用之前进行检查。

    对于记忆,你应该首先摆脱binary数组,之后你可以做这样的事情:

    int memo[NUM_ELEMENTS][MAX_SUM][MAX_K]; 
    bool subset_sum(int target, int i, int sum, int *a, int size, int K) {
                if (sum == target && !K) {
                        memo[i][sum][K] = true;
                        return memo[i][sum][K];
                } else if (sum < target && i < size) {
                        if (memo[i][sum][K] != -1)
                            return memo[i][sum][K];
    
    
                        memo[i][sum][K] = foo(target, i + 1, sum + a[i], a, size, K-1) || 
                                          foo(target, i + 1, sum, a, size, K);
    
                        return memo[i][sum][K]
                }
    
                return false;
        }
    

    然后,看看memo[_all indexes_][target][K]。如果这是真的,那么至少存在一种解决方案。您可以存储附加信息以获得下一个解决方案,或者您可以使用ifound_index - 1 迭代到0 并检查您拥有哪个i memo[i][sum - a[i]][K - 1] == true。然后对此进行递归,依此类推。这将允许您仅使用 memo 数组来重构解决方案。

    【讨论】:

    • 基本情况:memo[a[i]][0][0] = 1 对于其他所有情况 0 是否适合基本情况? paste.ofcode.org/sQuVRSvUQpTaDd3VZYpdWZ 此代码是否适合打印元素?一旦你让我知道你的意见,我会正确地标记你的答案。
    • @nomanpouigt 尚未对其进行测试,但代码看起来正确。基本情况应与原始递归相同。
    • @nomanpouigt 我在代码中有一个小错误,导致它只记住真实值。我现在修好了。
    • @IVlad: 可以添加基础案例吗?
    • @newbie_old 实际上,由于递归的工作原理,我认为没有必要明确设置任何基本情况。 “基本情况”是函数的第一个if。我已经稍微更新了答案。
    【解决方案2】:

    据我了解,如果只检查输入的可行性,问题可以用二维状态空间来解决

    bool[][] IsFeasible = new bool[n][k]
    

    其中IsFeasible[i][j]true 当且仅当存在1i 元素的子集,每个元素的总和恰好为j

    1 <= i <= n
    1 <= j <= k
    

    对于这个状态空间,递归关系

    IsFeasible[i][j] = IsFeasible[i-1][k-a[i]] || IsFeasible[i-1][k]
    

    可以使用,其中or-运算符||左侧对应选择i-th项,右侧对应not选择i-th 项目。通过回溯或评估过程中保存的辅助信息可以得到实际的项目选择。

    【讨论】:

    • 不,这不仅仅是可行性。您的解决方案与子集总和相同,但有一个额外的限制,即只有 K 个元素的总和为特定数字。
    • 确实,我误读了这个问题;我会尽快更新我的答案。
    猜你喜欢
    • 2022-11-28
    • 1970-01-01
    • 1970-01-01
    • 2023-02-07
    • 2012-12-30
    • 1970-01-01
    • 2012-11-30
    • 2019-03-02
    • 1970-01-01
    相关资源
    最近更新 更多