给定一个数组,找到小于c的n个数字的组合

时间:2015-12-07 17:22:32

标签: c arrays loops

这是一个艰难的,至少对于我的最低技能。

基本上,用户将价格列表输入到数组中,然后输入他想要购买的所需项目数,最后不超过最高成本。

我需要检查所需项目数的组合数量是否小于或等于给定的费用。

如果问题是组合中固定数量的项目,比如3,只需三个循环选择每个价格并添加它们进行测试就会容易得多。

我感到困难的是要求用户输入任意数量的项目,直到数组中的项目数。

这是我最初决定的,然后才意识到用户可以指定任意数字的组合,而不仅仅是三个。它是在这里的类似主题的帮助下创建的,但同样只有当用户指定他想要每个组合3个项目时它才有效。否则它不起作用。

// test if any combinations of items can be made
  for (one = 0; one < (count-2); one++) // count -2 to account for the two other variables
  {
    for (two = one + 1; two < (count-1); two++) // count -1 to account for the last variable
    {
      for (three = two + 1; three < count; three++)
      {
        total = itemCosts[one] + itemCosts[two] + itemCosts[three];
        if (total <= funds)
        {
          // DEBUG printf("\nMatch found! %d + %d + %d, total: %d.", itemCosts[one], itemCosts[two], itemCosts[three], total);
          combos++;
        }
      }
    }
  }

据我所知,根据用户所需的每个组合的项目数量,没有简单的方法可以灵活调整它。

我非常感谢给予的任何帮助。

2 个答案:

答案 0 :(得分:4)

展平嵌套迭代的一个技巧是使用递归。

创建一个函数,该函数包含到目前为止已选择的项目数组,以及到目前为止所拾取的项目数。算法应该是这样的:

  • 如果您选择了与目标N相等的项目数,请计算总和并按照限制进行检查
  • 如果您没有选择足够的项目,请在列表中再添加一项,然后进行递归通话。

为确保您不会选择相同的项目两次,请传递函数可以选择的最小索引。函数的声明可能如下所示:

int count_combinations(
    int itemCosts[]
,   size_t costCount
,   int pickedItems[]
,   size_t pickedCount
,   size_t pickedTargetCount
,   size_t minIndex
,   int funds
) {
    if (pickedCount == pickedTargetCount) {
        // This is the base case. It has the code similar to
        // the "if" statement from your code, but the number of items
        // is not fixed.
        int sum = 0;
        for (size_t i = 0 ; i != pickedCount ; i++) {
            sum += pickedItems[i];
        }
        // The following line will return 0 or 1,
        // depending on the result of the comparison.
        return sum <= funds;
    } else {
        // This is the recursive case. It is similar to one of your "for"
        // loops, but instead of setting "one", "two", or "three"
        // it sets pickedItems[0], pickedItems[1], etc.
        int res = 0;
        for (size_t i = minIndex ; i != costCount ; i++) {
            pickedItems[pickedCount] = itemCosts[i];
            res += count_combinations(
                itemCosts
            ,   costCount
            ,   pickedItems
            ,   pickedCount+1
            ,   pickedTargetCount
            ,   i+1
            ,   funds
            );
        }
        return res;
    }
}

你可以这样调用这个函数:

int itemCosts[C] = {...}; // The costs
int pickedItems[N]; // No need to initialize this array
int res = count_combinations(itemCosts, C, pickedItems, 0, N, 0, funds);

Demo.

答案 1 :(得分:2)

可以使用 backtracking 算法完成此操作。这相当于实现嵌套for loop的列表。通过尝试查看嵌套for循环序列的执行模式可以更好地理解这一点。

例如,假设您有一个3 for的序列,并且代码执行已达到第三级(最里面)。在完成所有迭代之后,您将返回到第二级for,在那里您将进入下一次迭代,在该迭代中您再次跳到第三级for。类似地,当第二级完成所有迭代时,你跳回到第一级for,继续下一次迭代,你跳到第二级,然后跳到第三级。

因此,在给定的级别中,您尝试转到更深的一个(如果有的话),如果没有更多的迭代,则返回一个级别(回溯)。

使用回溯,您可以通过数组表示嵌套的for,其中每个元素都是索引变量:array [0]是for级别0的索引,并且等等。

以下是您的问题的示例实现:

#define NUMBER_OF_OBJECTS  10
#define FORLOOP_DEPTH       4 // This is equivalent with the number of
                              // of nested fors and in the problem is
                              // the number of requested objects
#define FORLOOP_ARRAY_INIT -1 // This is a init value for each "forloop" variable
#define true  1
#define false 0
typedef int bool;
int main(void)
{
    int object_prices[NUMBER_OF_OBJECTS];
    int forLoopsArray[FORLOOP_DEPTH];
    bool isLoopVariableValueUsed[NUMBER_OF_OBJECTS];
    int forLoopLevel = 0;

    for (int i = 0; i < FORLOOP_DEPTH; i++)
    {
        forLoopsArray[i] = FORLOOP_ARRAY_INIT;
    }
    for (int i = 0; i < NUMBER_OF_OBJECTS; i++)
    {
        isLoopVariableValueUsed[i] = false;
    }


    forLoopLevel = 0; // Start from level zero
    while (forLoopLevel >= 0)
    {
        bool isOkVal = false;

        if (forLoopsArray[forLoopLevel] != FORLOOP_ARRAY_INIT)
        {
            // We'll mark the loopvariable value from the last iterration unused 
            // since we'll use a new one (in this iterration)
            isLoopVariableValueUsed[forLoopsArray[forLoopLevel]] = false;
        }

        /* All iterations (in all levels) start basically from zero
         * Because of that here I check that the loop variable for this level
         * is different than the previous ones or try the next value otherwise
        */
        while (    isOkVal == false
                && forLoopsArray[forLoopLevel] < (NUMBER_OF_OBJECTS - 1))
        {
            forLoopsArray[forLoopLevel]++; // Try a new value
            if (loopVariableValueUsed[forLoopsArray[forLoopLevel]] == false)
            {
                objectUsed[forLoopsArray[forLoopLevel]] = true;
                isOkVal = true;
            }
        }

        if (isOkVal == true) // Have we found in this level an different item?
        {
            if (forLoopLevel == FORLOOP_DEPTH - 1) // Is it the innermost?
            {
                /* Here is the innermost level where you can test
                 * if the sum of all selected items is smaller than
                 * the target
                 */
            }
            else // Nope, go a level deeper
            {
                forLoopLevel++;
            }
        }
        else // We've run out of values in this level, go back
        {
            forLoopsArray[forLoopLevel] = FORLOOP_ARRAY_INIT;
            forLoopLevel--;
        }
    }
}