БлогNot. Подмассив с наибольшей суммой элементов или алгоритм Кадане для вектора и матриц...

Подмассив с наибольшей суммой элементов или алгоритм Кадане для вектора и матрицы

В типичной постановке задача звучит так:

Дана последовательность (массив) целых чисел, которые могут быть как отрицательными, так и положительными. Реализовать быстрый алгоритм поиска наибольшей суммы непрерывной последовательности элементов.

Лучше, чем алгоритмом Кадане с временной сложностью O(n) сделать вряд ли возможно.

Вот реализация, проверенная в консоли текущей сборки Visual Studio 2019:

#include <iostream>
#include <iterator>
#include <cstdint>

template <class InputIterator> 
intmax_t maxsum(InputIterator first, InputIterator last) {
 intmax_t maxsofar = 0, maxendinghere = 0;
 for (; first != last; ++first) {
  maxendinghere = std::max (maxendinghere + (intmax_t)(*first), (intmax_t)0);
  maxsofar = std::max (maxsofar, maxendinghere);
 }
 return maxsofar;
}

int main() {
 int a[] = { -1,1,2,3,4,0,-55,6,3 };
 std::cout << maxsum(std::begin(a), std::end(a)) << std::endl;
}

Намного ли ухудшится производительность, если мы захотим найти максимальную сумму прямоугольного подмассива в двумерном массиве (матрице) и определить границы этого подмассива?

У меня не получилось лучше, чем O(n3), ведь суммы алгоритмом Кадане придётся считать внутри двойного цикла, выбирающего нужные столбцы матрицы.

Исходный алгоритм не хранил информацию об индексах элементов, ограничивающих искомый подмассив, в реализацию для матрицы это добавлено. Также для простоты использован обычный тип данных int *, а не шаблон.

Среда выполнения была той же.

#include <iostream>
#include <climits> /* INT_MIN */
#include <cstring> /* memset */

int kadane(int* arr, int* start, int* finish, int n) {
 //Каданеподобный алгоритм для простого массива целых с сохранением
 //индексов начала и конца цепочки в *start, *finish
 int sum = 0, maxSum = INT_MIN, i;
 *finish = -1;
 int local_start = 0;
 for (i = 0; i < n; ++i) {
  sum += arr[i];
  if (sum < 0) {
   sum = 0; local_start = i + 1;
  }
  else if (sum > maxSum) {
   maxSum = sum;
   *start = local_start;
   *finish = i;
  }
 }
 if (*finish != -1) return maxSum; //был хотя бы один неотрицательный
 maxSum = arr[0]; //если все отрицательные
 *start = *finish = 0;
 for (i = 1; i < n; i++) {
  if (arr[i] > maxSum) {
   maxSum = arr[i];
   *start = *finish = i;
  }
 }
 return maxSum; //то макс. элемент - это ответ
}

#define ROW 4
#define COL 5
 /* просто для примера, чтобы не возиться с размерностями */

void findMaxSum (int M[][COL], int & maxSum,
 int& finalTop, int& finalLeft, int& finalBottom, int& finalRight) {
 int left, right, i, temp[ROW], sum, start, finish;
 maxSum = INT_MIN;
 for (left = 0; left < COL; ++left) { //Взять левый столбец
  memset(temp, 0, sizeof(temp));
  for (right = left; right < COL; ++right) { //Взять правый столбец
   for (i = 0; i < ROW; ++i) temp[i] += M[i][right];
    //Вычислить суммы между левым и правым столбцами для строк
   sum = kadane (temp, &start, &finish, ROW); //найти начало и конец нужной цепочки
   if (sum > maxSum) { //сохранить индексы, если нужно
    maxSum = sum; 
    finalLeft = left; finalRight = right; finalTop = start; finalBottom = finish;
   }
  }
 }
}

int main() {
 int M[ROW][COL] = { 
  { 1, 2, -1, -4, -20 },
    //искомая подматрица окружена пробелами:
  {-8,      -3, 4, 2,     1 },
  { 3,       8,10, 1,     3 },
  {-4,      -1, 1, 7,    -6 } 
 };
 int r1,c1,r2,c2,ms;
 findMaxSum(M,ms,r1,c1,r2,c2);
 std::cout << "From indexes (" << r1 << ", " << c1 << ")" << 
  " to (" << r2 << ", " << c2 << ")" << std::endl;
 std::cout << "Max sum is: " << ms << std::endl;
 return 0;
}

28.01.2022, 20:02 [716 просмотров]


теги: список c++ алгоритм

К этой статье пока нет комментариев, Ваш будет первым