БлогNot. C++: максимальная и минимальная цепочка элементов с чередованием признака

C++: максимальная и минимальная цепочка элементов с чередованием признака

Алгоритм проверки чередования признака в массиве - вещь хорошая, но не даёт ответа на вопрос, где именно началась серия и какова наибольшая или наименьшая длина серии элементов, на которой признак чередуется.

Если ищем максимальную цепочку (например, чисел, меняющих знак по отношению к своим соседям в массиве), соответствующая функция будет достаточно проста:

#define SIGN(a) ((a)<0?-1:((a)>0?1:0))

void max_alternating_series (int n, int *a, int &start, int &maxlen) {
 int z=SIGN(a[0]),i=1,len,s;
 start=-1;maxlen=0;
 while (i<n) {
  while (SIGN(a[i])==z) {
   z=SIGN(a[i]); i++; if (i==n) return;
  }
  s=i-1; len=1;
  while (SIGN(a[i])!=z) {
   len++; z=SIGN(a[i]); i++; if (i==n) break;
  }
  if (len>maxlen) { maxlen=len; start=s; }
 }
}

На вход функции подаётся размерность массива n и указатель на целочисленный массив a, результаты возвращаются через параметры start (номер элемента, с которого начинается серия, нумерация с нуля) и maxlen (максимальная длина цепочки).

С минимальной длиной цепочки всё не так однозначно. Например, для массива (-1, -1, 2, -3, 4, 4) что считать минимальной длиной серии - (-1, 2, -3, 4) или (-1, 2)? Поскольку второй случай тривиален - любая смена знака даст серию длиной 2, и это будет ответ, рассмотрим только первый - пока смена знака продолжается, серия не заканчивается.

Но в этом случае поиск минимальной цепочки мало чем отличается от максимальной, просто реализуем стандартный алгоритм поиска минимума вместо максимума:

void min_alternating_series (int n, int *a, int &start, int &minlen) {
 int z=SIGN(a[0]),i=1,len,s;
 start=-1;minlen=n+1;
 while (i<n) {
  while (SIGN(a[i])==z) {
   z=SIGN(a[i]); i++; if (i==n) return;
  }
  s=i-1; len=1;
  while (SIGN(a[i])!=z) {
   len++; z=SIGN(a[i]); i++; if (i==n) break;
  }
  if (len<minlen) { minlen=len; start=s; }
 }
}

Напишем тестовую программку для обеих функций:

#include <stdio.h>

void test (int n, int *a) {
 int start,len;
 printf ("\n");
 for (int i=0; i<n; i++) printf ("%d ",a[i]);
 min_alternating_series (n,a,start,len);
 printf ("\nMIN Start=%d, Len=%d",start,len);
 max_alternating_series (n,a,start,len);
 printf ("\nMAX Start=%d, Len=%d",start,len);
}

int main () {
 const int n=8;
 int a[n]={1,-2,3,-4,5,-6,7,-8};
 test (n,a);
 int b[n]={1,2,3,-4,5,-6,7,-8};
 test (n,b);
 int c[n]={1,2,3,4,5,6,7,-8};
 test (n,c);
 int d[n]={-1,2,3,4,5,6,7,8};
 test (n,d);
 int e[n]={1,2,3,4,5,-6,7,-8};
 test (n,e);
 int f[n]={-1,2,-3,4,5,6,7,8};
 test (n,f);
 int g[n]={1,2,-3,4,0,6,7,8};
 test (n,g);
 int h[n]={1,-2,3,4,-4,6,7,-8};
 test (n,h);
 int i[n]={-1,2,3,-4,5,6,-7,8};
 test (n,i);
 int j[n]={1,-2,3,-4,-5,6,0,-8};
 test (n,j);
 int k[n]={0,1,0,1,1,1,0,1};
 test (n,k);
 getchar();
 return 0;
}

И посмотрим, что она выдаёт:

1 -2 3 -4 5 -6 7 -8
MIN Start=0, Len=8
MAX Start=0, Len=8
1 2 3 -4 5 -6 7 -8
MIN Start=2, Len=6
MAX Start=2, Len=6
1 2 3 4 5 6 7 -8
MIN Start=6, Len=2
MAX Start=6, Len=2
-1 2 3 4 5 6 7 8
MIN Start=0, Len=2
MAX Start=0, Len=2
1 2 3 4 5 -6 7 -8
MIN Start=4, Len=4
MAX Start=4, Len=4
-1 2 -3 4 5 6 7 8
MIN Start=0, Len=4
MAX Start=0, Len=4
1 2 -3 4 0 6 7 8
MIN Start=1, Len=5
MAX Start=1, Len=5
1 -2 3 4 -4 6 7 -8
MIN Start=6, Len=2
MAX Start=0, Len=3
-1 2 3 -4 5 6 -7 8
MIN Start=0, Len=2
MAX Start=2, Len=3
1 -2 3 -4 -5 6 0 -8
MIN Start=0, Len=4
MAX Start=0, Len=4
0 1 0 1 1 1 0 1
MIN Start=5, Len=3
MAX Start=0, Len=4

Как видите, некоторые тесты (где искомая цепочка смены знаков не единственна) отличаются.

Ноль в массиве считается сменой знака. Если это не так, поменяйте макрос SIGN. Если есть несколько одинаковых серий, функции найдут первую из них. Для поиска последней серии поменяйте в них проверки if (len<minlen) на if (len<=minlen) и if (len>maxlen) на if (len>=maxlen)

Вместо SIGN можно написать другой макрос (функцию) проверки нужного свойства у очередного элемента массива.

15.11.2013, 21:51 [10171 просмотр]


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

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