簡體   English   中英

給定一個數組,找到小於c的n個數字的組合

[英]Given an array, find combinations of n numbers that are less than c

這是一個艱難的,至少我的最低技能。

基本上,用戶將價格列表輸入到陣列中,然后輸入他想要購買的所需數量的項目,最后是不超過的最大成本。

我需要檢查所需數量的項目的組合數量是否小於或等於給定的成本。

如果問題是組合中固定數量的項目,比如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++;
        }
      }
    }
  }

據我所知,根據用戶所需的每個組合的項目數量,沒有簡單的方法可以使其靈活適應。

我真的很感激任何幫助。

扁平嵌套迭代的一個技巧是使用遞歸。

創建一個函數,該函數包含到目前為止您選擇的項目數組,以及您到目前為止所選擇的項目數。 算法應該是這樣的:

  • 如果您已選擇等於目標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);

演示。

這可以通過使用回溯算法來完成。 這相當於實現嵌套for loop列表。 通過嘗試查看嵌套for循環序列的執行模式可以更好地理解這一點。

例如,假設你有一個3 for s序列,代碼執行已達到第三級(最里面)。 在此之后經過其所有迭代返回到第二個層次for您去您在其中第三級再跳到下一個迭代,其中for 同樣地,當第二級完成其所有的迭代你跳回到第一級for它繼續與您在其中第二跳級,並從那里在第三下一次迭代。

因此,在給定的級別中,您嘗試轉到更深的一個(如果有的話),如果沒有更多的迭代,則返回一個級別(后退軌道)。

使用回溯您所代表的嵌套for由陣列,其中每個元素是一個索引變量:數組[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--;
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM