Задача о куче камней. Перебор

Задача такая: Имеется N камней, известны их веса Pi (i=1...N), задано количество куч M. Требуется разложить камни на M куч так, чтобы минимизировать вес самой тяжелой кучи. Решала эту задачу таким способом: в кучу с наименьшим весом кладем самый тяжелый камень. Я же правильно понимаю, что это эвристический алгоритм? Мне нужно решить задачу именно перебором, но нет идей как сделать это для неопределенного числа куч.


Ответы (1 шт):

Автор решения: nevilad

Перебор не самая эффективная стратегия решения задач, но если нужно решить перебором, то можно сделать это через рекурсию. Функция StoneBruteForce получает на вход:

iStone - номер текущего камня;

stoneWeights - вектор весов камней (Pi из условия задачи);

heaps - вектор представляющий кучи, его элементы векторы в которых хранятся веса хранимых там камней. Он имеет размер M из условия задачи;

и возвращает вес самой тяжелой кучи и распределение камней по кучам в выходном аргументе answer:

    typedef double stone_weight;
    
    stone_weight StoneBruteForce(int iStone, const std::vector<stone_weight>& stoneWeights,
      std::vector<std::vector<stone_weight>>& heaps,
      std::vector<std::vector<stone_weight>>& answer)
    {
      stone_weight maxHeapWeight = numeric_limits<stone_weight>::max();
      if (iStone == stoneWeights.size() - 1)
      {
        //Последний камень - кладем его поочередно во все кучи, считаем их 
        //веса и запоминаем ответ, если вес самой тяжелой кучи меньше чем 
        //известный.
        for (std::vector<std::vector<stone_weight>>::iterator it = heaps.begin();
             it != heaps.end(); ++it)
        {
          it->push_back(stoneWeights[iStone]);
    
          //Посчитать веса всех куч и взять кучу с самым большим весом.
          stone_weight localMaxHeapWeight = 0.0;
          for (std::vector<std::vector<stone_weight>>::iterator itH = heaps.begin();
               itH != heaps.end(); ++itH)
          {
            stone_weight heapWeight = accumulate(itH->begin(), itH->end(), 0.0);
            if (heapWeight > localMaxHeapWeight)
              localMaxHeapWeight = heapWeight;
          }
    
          //Если самая тяжелая легче чем текущая самая тяжелая, запомним ее.
          if (maxHeapWeight > localMaxHeapWeight)
          {
            maxHeapWeight = localMaxHeapWeight;
            answer = heaps;
          }
          it->pop_back();
        }
      }
      else
      {
        //Не последний камень - кладем его поочередно во все кучи и продолжаем
        //рекурсию со следующим камнем.
        for (std::vector<std::vector<stone_weight>>::iterator it = heaps.begin();
             it != heaps.end(); ++it)
        {
          it->push_back(stoneWeights[iStone]);
          std::vector<std::vector<stone_weight>> localanswer;
          stone_weight localMaxHeapWeight = StoneBruteForce(iStone + 1, 
                                         stoneWeights, heaps, localanswer);
          if (maxHeapWeight > localMaxHeapWeight)
          {
            maxHeapWeight = localMaxHeapWeight;
            answer = localanswer;
          }
          it->pop_back();
        }
      }
      return maxHeapWeight;
    }
    
    void print_answer(stone_weight maxHeapWeight, 
                      std::vector<std::vector<stone_weight>>& answer)
    {
      int iHeap = 0;
      std::cout << "Max heap weight " << maxHeapWeight << std::endl;
      for (auto h : answer)
      {
        std::cout << "Heap " << iHeap << " stones";
        for (auto w : h)
          std::cout << " " << w;
        std::cout << std::endl;
        iHeap++;
      }
    }

Вызываем так

  std::vector<stone_weight> stoneWeights = { 1,2,3,4,5,6,7,8,9 };
  std::vector<std::vector<stone_weight>> heaps(3);
  stone_weight maxHeapWeight = StoneBruteForce(0, stoneWeights, heaps, answer);
  print_answer(minWeightDiff, answer);

Введенные пользователем веса камней надо положить в stoneWeights и изменить размер heaps на количество куч.

→ Ссылка