2384. Сортирующая игра

 

В Сортирующей Игре изначально задана перестановка чисел от 1 до n включительно. За один ход можно выбрать любые k последовательно стоящих чисел и развернуть их в обратном порядке. Найти наименьшее количество ходов, за которое можно отсортировать все числа в порядке возрастания.

 

Вход. Состоит из нескольких тестов. Первая строка каждого теста содержит два целых числа n (2 ≤ n ≤ 8) и k (2 ≤ k n). Вторая строка каждого теста содержит перестановку чисел от 1 до n.

 

Выход. Для каждого теста вывести в отдельной строке наименьшее количество ходов, за которое можно отсортировать все числа в порядке возрастания. Если сортировка невозможна, то вывести -1.

 

Пример входа

Пример выхода

3 3

1 2 3

3 3

3 2 1

8 4

7 2 1 6 8 4 3 5

5 4

3 2 4 1 5

0

1

7

-1

 

 

РЕШЕНИЕ

графы - поиск в ширину

 

Анализ алгоритма

Задачу будем решать при помощи поиска в ширину. Сохраним исходную перестановку в массиве board и занесем в очередь поиска в ширину. После извлечения очередной перестановки из очереди будем перебирать в ней все возможные k последовательно стоящих чисел, обращать их и класть в очередь.

 

Реализация алгоритма

Входную перестановку будем хранить в массиве permut. В массив Sorted занесем числа от 1 до n в порядке возрастания – это перестановка, которую следует получить. Для поиска в ширину используем очередь перестановок q. Отображение m хранит расстояние от начальной перестановки до текущей.

 

deque<vector<int> > q;

map<vector<int>,int> m;

vector<int> permut, Sorted;

 

Функция fewestMoves возвращает наименьшее количество ходов, за которое можно отсортировать все числа в порядке возрастания.

 

int fewestMoves(void)

{

 

Начальную перестановку permut кладем в очередь и запускаем поиск в ширину.

 

  q.push_back(permut);

  while(!q.empty())

  {

    vector<int> pos = q[0];

    q.pop_front();

 

Если текущая перестановка pos уже отсортирована (равна Sorted), то возвращаем m[pos] – наименьшее количество ходов, за которое можно получить pos.

 

    if (pos == Sorted) return m[pos];

 

Перебираем все возможные варианты k последовательно стоящих чисел (подпоследовательность p[i, …, i + k], где i + kn) и разворачиваем их. Если полученная перестановка не встречалась ранее (не занесена в отображение m), то вычисляем расстояние до нее (минимальное количество ходов, за которое ее можно получить) и заносим в конец очереди.

 

    for(int i = 0; i + k <= n; i++)

    {

      vector<int> p = pos;

      reverse(p.begin() + i,p.begin() + i + k);

      if (m.count(p) == 0)

      {

        m[p] = m[pos] + 1;

        q.push_back(p);

      }

    }

  }

 

Если просмотрены все перестановки, но отсортированная последовательность чисел не получена, то возвращаем -1.

 

  return -1;

}

 

Основная часть программы. Читаем входные данные. Строим отсортированный массив Sorted.

 

while(scanf("%d %d",&n,&k) == 2)

{

  Sorted.clear(); permut.clear();

  m.clear(); q.clear();

  for(i = 0; i < n; i++)

  {

    Sorted.push_back(i+1);

    scanf("%d",&value);

    permut.push_back(value);

  }

 

Запускаем поиск в ширину и выводим результат.

 

  res = fewestMoves();

  printf("%d\n",res);

}

 

Улучшенная реализация

Можно значительно ускорить работу программы, изменив структуры хранения данных. При поиске в ширину в очереди q будем хранить не перестановки в виде массивов, а их коды – целые числа. Пусть id – код перестановки, полученной в процессе поиска в ширину. Тогда m[id] хранит наименьшее количество операций разворота, после выполнения которых можно получить из исходной перестановки перестановку с кодом id. Исходная перестановка хранится в массиве permut.

 

deque<int> q;

map<int,int> m;

int permut[8];

 

Перестановку, заданную в массиве а, будем кодировать целым n-цифровым числом. Код перестановки возвращается функцией ID.

 

int ID(int *a)

{

  int id = 0;

  for (int i = 0; i < n; i++) id = id * 10 + a[i];

  return id;

}

 

Код id преобразуется в массив mas (каждую цифру числа id заносим в отдельную ячейку массива mas).

 

void getAarray(int id, int *mas)

{

  for (int i = n - 1; i >= 0; i--, id /= 10)

    mas[i] = id % 10; 

}

 

На вход функции fewestMoves подается перестановка permut и значение k.

 

int fewestMoves(int *permut, int k)

{

  int i, j, a[8], b[8];

  q.clear(); m.clear();

 

Заносим в очередь q код входной перестановки permut.

 

  q.push_back(ID(permut));

  while(!q.empty())

  {

 

Извлекаем код перестановки из головы очереди. Преобразуем ее в массив a.

 

    int id = q[0]; q.pop_front();

    getAarray(id, a);

 

Проверим, не расположены ли элементы текущей перестановки в возрастающем порядке. Если да, то останавливаем поиск и возвращаем искомое наименьшее количество ходов, хранящееся в m[id].

 

    for(i = 0; i < n; i++)

      if (a[i] != i + 1) break;

    if (i == n) return m[id];

 

Совершаем все возможные развороты k рядом стоящих чисел.

 

    for(i = 0; i + k <= n; i++)

    {

      for(j = 0; j < n; j++) b[j] = a[j];

      reverse(b + i, b + i + k);

      int idB = ID(b);

      if (m[idB] == 0)

      {

        m[idB] = m[id] + 1;

        q.push_back(idB);

      }

    }

  }

  return -1;

}

 

Основная часть программы. Для каждого теста читаем входные данные и выводим ответ.

 

while(scanf("%d %d",&n,&k) == 2)

{

  for(i = 0; i < n; i++)

    scanf("%d",&permut[i]);

  res = fewestMoves(permut,k);

  printf("%d\n",res);

}