Разделите массив на k непрерывных разделов так, чтобы сумма максимального раздела была минимальной

Здесь подмножество максимальной суммы является одним из k подмножеств, которые дают максимальную сумму, например: arr = [10,5,3,7] и k = 2 возможных способа разделить arr на k подмножеств: {10, [5,3,7]}, {[10,5], [3,7}, {[10,5,3], 7} и {[10,5], [3,7} - оптимальный вариант. Изменить: это эквивалент https://www.codechef.com/DI15R080/problems/MINMAXTF

7 ответов

Предположим, вы знаете, что ответ равен x, что означает, что сумма максимального подмножества равна x. Вы можете проверить это предположение с помощью жадного алгоритма O (n). (Обходите массив слева направо и выбирайте элементы, пока сумма этого подмножества не станет меньше x). Теперь вы можете выполнить бинарный поиск по x и найти минимальное значение для x. Сложность этого алгоритма O (nlogn).

Это пример двоичного поиска в пробном пространстве.

int min_max_sum(std::vector<int> & a){


    int n=a.size();

    long long high=0,low=0;
    for (int i = 0; i < n; ++i)
    {
        high+=a[i];
        low=max(a[i],low);
    }

    while(low<=high){
        long long mid = (low+high)/2;

        long long part_sum=0;
        int parts=0;
        for (int i = 0; i < n; ++i)
        {
            if (part_sum+a[i]>mid)
            {
                part_sum=0;
                parts++;
            }else{
                part_sum+=a[i];
            }
        }
        if (parts<k)
        {
            low=mid+1;
        }else{ //if we have got exactly k parts or more, keep on decreasing the value of sum of subarray
            high=mid-1;
        }
    }

    return high;
}

сложность: O(n log(сумма (массив))).

Но поскольку логрифмы экспоненциально лучше линейных, эта сложность довольно хорошая.

сложность наихудшего случая: O(n log(INT_MAX * n))=O(32 n +n log(n))=O(n log(n)).

Начнем с примера. Предположим, N=5 и k= 3 (при условии, что после деления будет три части). Пусть наш массив будет {1,2,8,4,9}. Мы можем заметить, что если бы k было равно 1, то сумма максимального разбиения была бы суммой (все элементы массива), т. Е. 24, а если k= 5, то сумма максимального разбиения была бы max(все элементы массива), то есть 9. Теперь мы можем наблюдать, что с увеличением k сумма минимального значения максимального разбиения уменьшается. Наш алгоритм при этом использует бинарный поиск. Но как это сделать????? Посмотрев на вопрос - мы можем видеть, мы должны найти минимум максимума, который вызывает чувство проблем, таких как - "FFFFTTTTTTTT", где мы должны найти первый T. Мы собираемся сделать то же самое, но возьмем помощь функции в этом.(Посмотрите на код и ответьте отсюда, параллельно). Вспомогательная функция найдет значение K, когда будет предоставлена ​​минимальная сумма максимального раздела. Мы инициализируем low=max(все элементы массива), здесь low=9 и high=sum(все элементы массива), т. е. high=24. Поэтому mid=16, поэтому для min_max_dist=16 наша вспомогательная функция вернет требуемое количество k.Если число k>K, это означает, что наш min_max_dist может вместить больше значений. Таким образом, мы будем увеличивать наше низкое значение до середины +1, и если число k<= K, это означает, что за меньшее количество разделов мы можем достичь этого min_max_dist,, но мы можем сделать больше разделов и, следовательно, мы можем уменьшить высокое значение до середины. Итак, наш код будет выполняться на нашем примере следующим образом:-

    low           high               mid       number_of_k
    9             24                 16         9
    9             16                 12         2
    9             12                 10         4
    11            12                 11         3

и наконец наш результат->low=11 будет возвращен..

    #include <bits/stdc++.h>
    #define ll long long int
    using namespace std;

    ll number_of_k(ll arr[],ll n,ll minimum_max__dist){
       ll sum=0;
       ll k=1;
       for(ll i=0;i<n;i++){
          sum+=arr[i];
         if(sum > minimum_max__dist){
           sum=arr[i];
            k++;
          }
      }
    return k;
   }

    ll Max(ll arr[], ll n)
    {
       ll max1 = -1;
       for (ll i = 0; i < n; i++)
          if (arr[i] > max1)
              max1 = arr[i];
      return max1;
    }

    ll Sum(ll arr[], ll n)
    {
      ll sum = 0;
       for (int i = 0; i < n; i++)
       sum += arr[i];
       return sum;
     }

       ll min_max_bin_search(ll arr[],ll n,ll k){
         ll low=Max(arr,n);
         ll high=Sum(arr,n);
         ll mid;
while(low<high){
     mid=low+(high-low)/2;
    if(number_of_k(arr,n,mid)<=k)
       high=mid;
    else
        low=mid+1;
     }
  return low;
  }


 int main()
 {
      ll n,k;
       cin>>n>>k;
       ll arr[n];
       for(ll i=0;i<n;i++)cin>>arr[i];
       cout<<min_max_bin_search(arr,n,k)<<endl;

   return 0;
 }

Временная сложность этого алгоритма is->O(nlogn)

Прочтите эту статью в разделе Двоичный поиск-> https://www.topcoder.com/community/data-science/data-science-tutorials/binary-search/ и решите эту проблему-> http://www.spoj.com/problems/AGGRCOW/

Вы можете найти хорошую рецензию на решение, основанное на динамическом программировании, здесь: https://www.geeksforgeeks.org/painters-partition-problem/ и его сложность равна O(k*N^2).

Чтобы получить лучшее решение, вы можете использовать метод двоичного поиска, предложенный другими. Вы можете найти более подробное решение, использующее бинарный поиск, здесь: https://www.geeksforgeeks.org/painters-partition-problem-set-2/ и его сложность равна O(NlogN).

Это можно решить с помощью динамического программирования:

Давайте сначала определимся DP[n,m] быть оптимальным решением для деления подмассива C[1..n] в m частей. Где каждая часть имеет хотя бы один элемент.

DP[n,1] = sum(C1:Cn)
DP[n,n] = max(C1:Cn)
DP[n,m] = min( sum(Ck:Cn) + DP[k-1,m-1] )
          where k goes from m to n

Объяснение:
DP[n,1] - Базовый случай, когда количество разделов 1 есть только один путь - все элементы остались (от 1 до n).
DP[n,n] - Всякий раз, когда количество разделов равно количеству элементов, оставленных в массиве, существует только один допустимый способ его разделения - каждый элемент в отдельном разделе, поэтому раздел с максимальной суммой является максимальным элементом в массиве.
DP[n,m] - Это главное решение. Мы не знаем точно, сколько элементов будет нашим следующим разделом, поэтому нам нужно пройтись по всем параметрам и получить от них минимум.

Разделение - это просто проблема грубой силы. Вы должны сосредоточиться на функции, чтобы минимизировать. Так что вы должны минимизировать отклонение от среднего.

int sum_arr(int *arr,int size)
{
    int res=0;
    while (size-->0)
       res+=*arr++;
   return res;
}
int   minimize( const int *array, int size)
{
    int i,j,cur_i;
    double dev, cur_min,avg=(double)sum_arr(array,size)/size;

    for (i=1;i<size-1;i++)
      {
         dev=abs(avg-sum_arr(array,i));
         dev+=abs(avg-sum_arr(array+i,size-i);
         if (dev<cur_min)
         {
              cur_min=dev;
               cur_i=i;
         }
      }
     return cur_i;
}

Простой код будет:(не проверено)

      // This code works only if you have to find in a contiguous segment

bool isPossible(vector<int>& nums, int mid, int k) {
  int n = nums.size(), Count = 1, Sum = 0;
  for(int i = 0; i < n; i++) {
    if(nums[i] > mid)  return false;
    Sum += nums[i];
    if(Sum > mid) {
      Count++;
      Sum = nums[i]
    }
  }
  return Count <= k;
}
int Func(vector<int>& nums, int k) {
  int n = nums.size();
  int low = 0, high = accumulate(nums.begin(), nums.end(), 0);
  while(low <= high) {
    int mid = (low + high) / 2;
    if(isPossible(nums, mid, k)) {
      ans = mid;
      high = mid - 1;
    }  else  {
      low = mid + 1;
    }
  }
  return ans;
}
Другие вопросы по тегам