查找特定总和的数字的所有组合

时间:2014-09-11 05:49:22

标签: c++ c algorithm

Array
A ={1,2,3}

对于Sum值= 5
可能的组合

{3,2} , {1,1,1,1,1} , {2,2,1} and all possiable one

这是我的方法:

int count( int S[], int m, int n )
{
    // m is the size of the array and n is required sum
    // If n is 0 then there is 1 solution (do not include any coin)
    if (n == 0)
        return 1;

    // If n is less than 0 then no solution exists
    if (n < 0)
        return 0;

    // If there are no coins and n is greater than 0, then no solution exist
    if (m <=0 && n >= 1)
        return 0;

    // count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1]
    return count( S, m - 1, n ) + count( S, m, n-S[m-1] );
}

我的方法缺点::必须一次又一次地重新计算多种组合。因此,sum的值非常高,所以它很慢。
我想用动态编程实现这个请给我一个解释如何存储计算值以便我可以重用它减少了我的计划时间

3 个答案:

答案 0 :(得分:2)

我会采用不同的方式:

  1. 生成硬币数组以匹配总和

    1. 属于一种硬币类型
      • 从最大的一个开始
      • 尽可能多地添加
      • 但总和必须为<=,然后是目标总和
      • 如果达到目标总和存储结果
    2. 递归调用下一个较低硬币的步骤1
      • 但请记住最后一个硬币阵列状态
      • 如果没有硬币= 1则有时结果无效
    3. 转到下一个组合
      • 恢复上一个硬币阵列状态
      • 从中删除最后一枚硬币
      • 如果没有要删除则停止
      • 否则重复第2步
  2. 计算排列/组合,如果订单也很重要

    • 因此,请采取有效的结果并根据您的问题规则进行置换
    • 从中获取更多解决方案
    • 然后尝试1中的每一种可能性都会更快。
  3. 示例(适用于1.):

    coins = { 5,2,1 }
    sum=7
    
    5 | 2 
    5 | -     | 1 1
    - | 2 2 2 | 1
    - | 2 2   | 1 1 1
    - | 2     | 1 1 1 1 1
    
    • |分隔递归图层
    • 每种硬币类型都有一个递归级别
    • 所以在这种情况下需要3个数组状态的内存(长度取决于目标总和)
    • 这是可以接受的(我看到这个问题的空间复杂度更差的解决方案)
    • 对于非常大的总和我会使用 RLE 进行内存保存并加快进程

    [edit1] C ++源代码

    //---------------------------------------------------------------------------
    void prn_coins(int *count,int *value,int coins) // just to output solution somewhere
        {
        int i;
        AnsiString s="";
        for (i=0;i<coins;i++)
         s+=AnsiString().sprintf("%ix%i ",count[i],value[i]);
        Form1->mm_log->Lines->Add(s);
        }
    //---------------------------------------------------------------------------
    void get_coins(int *count,int *value,int coins,int sum,int ix=0,int si=0)
        {
        if (ix>=coins) return; // exit
        if (ix==0)  // init:
            {
            ix=0; // first layer
            si=0; // no sum in solution for now
            for (int i=0;i<coins;i++) count[i]=0; // no coins in solution for now
            }
        //1. genere actal coint type value[]
        count[ix]=(sum-si)/value[ix];   // as close to sum as can
        si+=value[ix]*count[ix];        // update actual sum
        for(;;)
            {
            //2. recursion call
            if (si==sum) prn_coins(count,value,coins);
            else get_coins(count,value,coins,sum,ix+1,si);
            //3. next combination
            if (count[ix]) { count[ix]--; si-=value[ix]; }
            else break;
            }
        }
    //---------------------------------------------------------------------------
    void main()
        {
        const int _coins=3;             // coin types
        int value[_coins]={5,2,1};      // coin values (must be in descending order !!!)
        int count[_coins]={0,0,0};      // coin count in actual solution (RLE)
        get_coins(count,value,_coins,7);
        }
    //-------------------------------------------------------------------------
    
    • 此代码在我的硬件设置上耗时约3毫秒
    • 只需将prn_coins函数更改为您的打印形式(我使用的是VCL备忘录和AnsiSring)
    • 在此代码中,解决方案状态会自动重新写回以前的状态
    • 所以不需要进一步记忆(否则有必要在递归之前和之后复制计数数组)
    • 现在,如果符合以下条件,则必须进行排列步骤:
    • 每枚硬币都是独一无二的? (1 1 2 5)!=( 1 1 2 5)
    • 或只是硬币类型? (1 1 2 5)!=(1 2 1 5)
    • 在这种情况下,只需将排列代码添加到prn_coins函数...
    • 但这是另一个问题......

答案 1 :(得分:2)

对您的解决方案进行一个非常简单的更改就是添加&#34; memoization&#34;。

考虑到数组S修复了函数的结果,只取决于mn。因此,您可以进行以下小改动:

int count( int S[], int m, int n ) {
    ...
    if (cache[m][n] == -1) {
        cache[m][n] = count( S, m - 1, n ) + count( S, m, n-S[m-1] );
    }
    return cache[m][n];
}

这样,您只需为每对不同的值mn计算一次结果。

我们的想法是将(mn)索引的二维数组全部初始化为-1(意思是&#34;尚未计算&#34;)。当您要计算count中的值时,首先检查该值是否尚未计算,如果是这种情况,您还将结果存储在2d矩阵中,这样您就不会重新计算在将来再次出现。

答案 2 :(得分:0)

对于动态编程,您需要概括您的问题。让S(a, x)成为所有可能的值x的总和,仅使用从A开始的a值。您的原始问题是S(0, X)

由于你有一个带有两个参数的离散函数,你可以将它的结果存储在一个二维数组中。

有一些简单的案例:a = A.lengthX > 0没有解决方案。

该集只包含X = 0的空值。

现在,您应该找到其他情况的递归公式,并以这样的方式填充表格,即您已经计算了您所依赖的索引(提示:考虑向后循环)。