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

Подматрица с минимальной суммой элементов

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

Очевидно, что "нативный" полный перебор даст здесь вычислительную сложность порядка O(N4), применяя алгоритм Кадане для работы с одномерными массивами-столбцами, попробуем обойтись сложностью O(N3), кто меньше?

От типа данных в алгоритме ничего не зависит и легко сделать шаблоны функций, работающие с любым скалярным типом.

Консольная программа, показанная ниже, проверена в Visual Studio 2015.

#include <iostream> 
#include <iomanip> //для setw
#include <cstdlib> //для exit
#include <climits> //для INT_MAX
#include <windows.h> //для system
using namespace std;

void error(int n) {
 char *msg[] = {
  "OK. Normal shutdown",
  "Can't allocate memory for matrix!"
 };
 cout << endl << msg[n];
 system("pause>nul");
 exit(n);
}

void printMatrix(int **a, int n, int m) {
 for (int i = 0; i < n; i++) {
  cout << endl;
  for (int j = 0; j<m; j++) {
   cout << setw(5) << a[i][j];
  }
 }
}

int kadane (int* arr, int* start, int* finish, int n) {
 int sum = 0, minSum = INT_MAX, 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 < minSum) {
   minSum = sum;
   *start = local_start;
   *finish = i;
  }
 }
 if (*finish != -1) return minSum;
 minSum = arr[0];
 *start = *finish = 0;
 for (i = 1; i < n; i++) {
  if (arr[i] < minSum) {
   minSum = arr[i];
   *start = *finish = i;
  }
 }
 return minSum;
}

void findMinSumSubmatrix (int **M, int ROWS, int COLS, 
  int &finalTop, int &finalLeft, int &finalBottom, int &finalRight, int &minSum) {
 minSum = INT_MAX;
 int left, right, i;
 int *temp = new int [ROWS];
 if (!temp) error (1);
 int sum, start, finish;
 for (left = 0; left < COLS; ++left) {
  for (int k=0; k<ROWS; k++) temp[k] = 0; 
   //каждый раз temp сбрасывать в 0, можно быстрей через memset
  for (right = left; right < COLS; ++right) {
   for (i = 0; i < ROWS; ++i) temp[i] += M[i][right];
   sum = kadane(temp, &start, &finish, ROWS);
   if (sum < minSum) {
    minSum = sum;
    finalLeft = left; finalRight = right;
    finalTop = start; finalBottom = finish;
   }
  }
 }
 delete[] temp;
}

int main() {
 //Описание данных и выделение памяти для матрицы:
 const int ROWS = 4, COLS = 5;
 int **M = new int * [ROWS];
 if (!M) error (1);
 for (int i = 0; i < ROWS; i++) {
  M[i] = new int [COLS];
  if (!M[i]) error(1);
 }
 
 //Заполнение матрицы числами по списку:
 int k = 0;
 int V[ROWS*COLS] = { 
   1, 2, 3, 4, 5,
   6,-1,-2,-3, 7,
   8,-4,-5,-6, 9,
  10,11,12,13,14
 };
 for (int i = 0; i < ROWS; i++)
 for (int j = 0; j < COLS; j++) M[i][j] = V[k++];

 //Обработка и вывод:
 int finalTop, finalLeft, finalBottom, finalRight, minSum;
 findMinSumSubmatrix(M,ROWS,COLS, finalTop, finalLeft, finalBottom, finalRight, minSum);
 cout << endl << "Matrix:";
 printMatrix (M, ROWS, COLS);
 cout << endl << "from indexes (" << finalTop << "," << finalLeft << 
   ") to (" << finalBottom << "," << finalRight << "), min.sum = " << minSum;

 //Освобождение памяти и выход:
 for (int i = ROWS-1; i>-1; i--) delete[] M[i];
 delete[] M;
 error(0);
}

Вот что выводится для теста из листинга:

Matrix:
    1    2    3    4    5
    6   -1   -2   -3    7
    8   -4   -5   -6    9
   10   11   12   13   14
from indexes (1,1) to (2,3), min.sum = -21
OK. Normal shutdown

11.11.2018, 13:34 [2009 просмотров]


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

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