воскресенье, 19 сентября 2010 г.

133. Border.

Ссылка на задачу здесь.

В качестве разминочной эта задача очень даже ничего. Для решения её нам необходимо знать всего лишь как быстро отсортировать список отрезков по их началу и применить кое-какие общие рассуждения.
Для удобства сортировки и отсутствия желания писать компаратор можно воспользоваться стандартной структурой pair<int,int> где будет хранится начальная и конечная точки отрезка.
Имея отрезки отсортированными очевидно, что взяв некий i-тый отрезок не один из последующих отрезков не может располагаться левее, в данном случае можно точку начала отрезка можно перенести на последующий отрезок, а в качестве конца выбрать max из координат концов. Логику работы можно проследить на следующем куске кода:


  1.   sort(mas.begin(),mas.end());
  2.   int answer = 0;
  3.   int begin = mas[0].first;
  4.   int end = mas[0].second;
  5.  
  6.   for(int i=1;i<mas.size();i++)
  7.   {
  8.     if (mas[i].second < end)
  9.       answer ++;
  10.     else
  11.       if (mas[i].first > begin)
  12.       {
  13.         begin = mas[i].first;
  14.         end = mas[i].second;
  15.       }
  16.       else end = mas[i].second;
  17.   }
* This source code was highlighted with Source Code Highlighter.
Полный код решения задачи - здесь.

вторник, 14 сентября 2010 г.

125. Shtirlits. Немножко о графах и переборе.

Полный текст задачи доступен здесь.

После прочтения задачи и ограничений на количество ячеек равное максимум 9 в голову приходит переборное решение, но тут возникает вопрос какой именно должен быть перебор. Если пробовать перебирать все возможные значения A [i, j] то очевидно, что число порядка pow(10,9) во временные ограничения не уложится.

Попробуем перебирать не сами числа в позиции [i][j], а выбирать те соседние ячейки в которых числа могут оказаться больше чем в текущей, по условию задачи максимальное число в этом случае может быть 4 для каждой ячейки(Для угловых - 2, для центральной [1][1] - 4, а для всех остальных 3). Очевидно что перебор в данном случае не должен занять много времени.
На выходе перебора имеем для каждой ячейки конкретные ячейки значения в которых больше чем в текущей.
Примерно это выглядит вот так в первом приближении:

  1. void solve(int pos,int lastMove)
  2. {
  3.   if (pos == n * n)
  4.   {
  5.     if (BuildSolution())
  6.     {
  7.       output();
  8.       exit(0);
  9.     }
  10.   }
  11.   else
  12.   {
  13.     int x = pos / n;
  14.     int y = pos % n;
  15.     int steps = mas[x][y];
  16.     if (steps)
  17.     {
  18.       for(int i = lastMove + 1;i < 4; i++)
  19.       {
  20.         int nextX = x + movx[i];
  21.         int nextY = y + movy[i];
  22.         int vertFrom = pos, vertTo = GetVert(nextX, nextY);
  23.         if (isValid(nextX, nextY) && !adj[vertTo][vertFrom])
  24.         {
  25.           mas[x][y]--;
  26.           adj[vertTo][vertFrom] = 1;
  27.           solve(pos,i);
  28.           adj[vertTo][vertFrom] = 0;
  29.           mas[x][y] ++;
  30.         }
  31.       }
  32.     }
  33.     else solve(pos+1,-1);
  34.   }
  35. }
* This source code was highlighted with Source Code Highlighter.
Процедура solve принимает два аргумента первый из которых указывает на текущее положение перебираемого элемента, замечу что pos <= n*n, а для вычисления положения элемента в матрице используются конструкции в строках 13 и 14, а второй аргумент это последний тип движения который был использован(movx, movy - стандартные массивы движений, для перемещения строго по горизонтали или вертикали).
Далее имеем в матрице adj дугу между вершинами i и j, только в том случае если значение в матрице A[i/n][i%n] > A[j/n][j%n], где n - размерность поля, а А матрица расстановки войск.
Между некоторыми ячейками, находящимися в зоне доступной для перемещения нет дуг, а значит значения в исходной матрице А для этих элементов равны. Т.е. иными словами далее нам необходимо сконденсировать граф объединяя в группу вершины доступные при перемещении, но не имеющие дуг:

void DfsGroupSeparation(int vert, int group)
{
  used[vert] = 1;
  groups[vert] = group;
  for(int i=0;i<n*n;i++)
    if (ByStep(vert,i) && !used[i] && !adj[vert][i] && !adj[i][vert])
      DfsGroupSeparation(i, group);
}

for(int i=0;i<n*n;i++)
    if (!used[i])
      DfsGroupSeparation(i,i);


* This source code was highlighted with Source Code Highlighter.
Задача почти решена, остаётся лишь на полученном графе построить топологический порядок и аккуратно вывести ответ. Фрагмент построения порядка:

  1. void DfsTopologicalSort(int group)
  2. {
  3.   used[group] = 1;
  4.   for(int i=0;i<n*n;i++)
  5.   {
  6.     if (groups[i] == group) // find all verticies from the same group
  7.     {
  8.       for(int j=0;j<n*n;j++)
  9.       {
  10.         if (adj[i][j])
  11.         {
  12.           if (!used[groups[j]])
  13.             DfsTopologicalSort(groups[j]);
  14.           else
  15.             if (used[groups[j]] == 1)
  16.             {
  17.               isCycle = true;
  18.               break;
  19.             }
  20.         }
  21.       }
  22.     }
  23.   }
  24.   used[group] = 2;
  25.   order.push_back(group);
  26. }
  27.  
  28. for(int i=0;i<n*n;i++) // for all vertices
  29.   {
  30.     if (!used[groups[i]])
  31.     {
  32.       DfsTopologicalSort(groups[i]);
  33.       if (isCycle)
  34.         return false;
  35.     }
  36.   }
* This source code was highlighted with Source Code Highlighter.

И в заключении не забыть выводить NO SOLUTION как это приведено в условии задачи.

понедельник, 13 сентября 2010 г.

Как использовать и чем стоит закусывать...

Вот и настало время для первого поста в моём блоге. Отнюдь это не будет кулинарный блог или для тех любителей кайпериньи с хорошей кашасой мегафуло и свежим лаймом...
Данный блог будет большей частью посвящён разбору наиболее интересных задач и алгоритмов с известного сервера задач acm.sgu.ru. Как мне кажется именно здесь можно найти хоть и небольшое, но в тоже время уникальное множество задач олимпиадного программирования. На текущий момент не предполагается какого-то упорядочивания задач по уровню сложности или по тематикам, материал будет излагаться по мере решения той или иной задачи.

Итак, приступим к трапезе:
В качестве обеденного филе миньён хотелось бы изведать задачу 122. The Book. На мой взгляд задача интересная так как заставляет подумать.
Сразу стоит обратить внимание, что формулировка задачи точь-в-точь как и задача поиска Гамильтонова цикла на графе. Как известно эта задача относится к числу NP полных, а ограничение говорит нам о 1000 вершин в графе, так что же делать... Замечу сразу, что ответ No Solution - невозможен, ибо согласну теореме Дирака, формулировку которой можно почитать всё на той же wiki статье, для данного графа всегда существует Гамильтонов цикл... Ну чтож дело за малым - отыскать его.
Как будем действовать?
Итеративно будем делать следующее.
1) Очевидно, что можно найти простой путь состоящий из (n+1)/2 вершин, итак получим текущее состояние в минимум (n+1)/2 вершин.
На каждом шаге, если можно продолжить путь из конечной вершины пути, либо начать из начальной, будем расширять ответ. Т.е. имея последовательность вершин X1,X2,X3,...,Xk мы можем либо перейти в состояние X1,X2,X3,...,Xk,Xk+1 такое, что каждая из вершин в данной последовательности смежна предыдущей, либо в X0,X1,X2,X3,...,Xk.
В случае если невозможно совершить вышеозначенные переходы, то

2) Возьмём одну из непросмотренных ранее вершин(т.е. не входящую в текущий ответ).
   2.1) Если конечная и начальная вершина текущего пути - смежны, то мы можем перестроить путь с учётом новой добавленной вершины. Допустим имеем X1,X2,X3,...,Xk и взята вершина Xi не просмотренная ранее. Очевидно, что хотя бы одна из вершин X1,.., Xk смежна Xi (k>=(n+1)/2), например Xm. Поэтому новый путь может быть получен как Xi,Xm,Xm-1,...,X1,Xk,Xk-1..Xm-1. Таким образом получим путь на 1 вершину длиннее.
  2.2) X1 и Xk не смежны. Тогда необходимо перестроить текущий путь таким образом что бы он по прежнему оставался простым и новые X1 и Xk были смежны и далее применить пункт 2.1. Предлагаю самостоятельно продумать этот момент, в задаче он является достаточно важным.

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