решение1

advertisement
Разбор.
Это, пожалуй, самая интересная задача, которую приходится здесь разбирать. Она отличается от
остальных повышенной сложностью. Данное решение в корне отличается от авторского и не претендует
быть лучшим. Однако оно имеет право на существование, так как решает данную задачу.
Для решения этой задачи нам потребуется структура данных дек. Дек представляет собой очередь с
одной стороны и стек с другой. Применяется крайне редко, но «метко».
Пусть для определенности слева будет очередь, а справа – стек. Это значит, что снимать элементы дека
теперь можно только спереди, а помещать как вперед, так и назад.
Идея решения основана на обходе в ширину, но сильно модифицированном и оптимизированном, так
как ограничения на время не позволяют писать его «в лоб». При реализации последнего метода нам
придется как бы имитировать Васины действия, что будет работать очень долго. Представьте только, 100 *
100 обходов в ширину, и каждый из них для всех картинок размерностью от 1*1 до 100*100. Время работы
колоссальное.
Представим картинку в виде графа, вершинами которого являются пиксели (дальше - клетки). Каждый
пиксель может быть связан с четырьмя другими по указанным правилам, это будут ребра. Заметим сразу,
что в подобных задачах преобразовывать картинку в непосредственно граф не стоит! Можно пользоваться
алгоритмом, оставаясь «в картинке», то есть переходить от одной вершины к другой по смежным ребрам (в
данном случае, геометрическим частям клетки) пикселей.
Возьмем на рассмотрение произвольную клетку (вершину), которую ещё не брали, и метку, изначально
равную числу какому-нибудь а. Пусть это черная клетка (для определенности). Обыкновенным обходом в
глубину пометим все черные клетки (вместе с исходной), до которых можно добраться по смежным
клеткам, то есть, обнаружив очередную подходящую клетку, будем помещать в дек вперед и помечать
числом а. Но, при просмотре соседних клеток, мы невольно будем натыкаться (если это будет в тесте) на
белые клетки. Не игнорировать же их нам!.. Так будем помещать их в дек назад (если они конечно ещё
непросмотренны), чтобы потом к ним вернуться, одновременно помечая их числом а+1. В этом состоит
«изюминка» метода: мы одновременно используем два обхода – в ширину и в глубину посредством дека. И
так действуем дальше: достали из дека клетку, обошли всех смежные с ней клетки, помечая их ее числом, а
если «заглядываем» в клетки другого цвета, добавляем их в конец дека, и метим числом на 1 большим.
Обойдя наконец все клетки(пиксели, вершины графа) картинки, мы получим матрицу с числами.
Максимальное из них следует запомнить, так как это один из вариантов будущего ответа.
Будем повторять это действие для каждой клетки картинки по очереди, начиная с а = 0. Минимальное
из максимальных запомненных величин и есть ответ на задачу. Ведь действительно, каждое число
характеризует количество переходов на другой цвет поля, что эквивалентно нажатию на начальный
пиксель (тот, с которого начат очередной обход).
Теперь опишем оптимизации, позволяющие ускорить программу. Заметим, что вовсе незачем
прогонять алгоритм для каждой из клеток. Ведь если мы начинаем просмотр с какой-либо клетки и
отмечаем сначала те клетки, до которых можно добраться из нее. Они будут с ней одного «номера» а. Так
зачем нам в дальнейшем проверять эти клетки ещё раз, если они выполнят эту же работу? Пометим их так,
чтобы было ясно, что с них начинать очередной обход не нужно. Это избавит программу от
многочисленных повторных пробегов.
Так же имеет смысл написать «заочное» решение для частных случаев.
Код программы приведен ниже.
#include <iostream>
1
#include <algorithm>
using namespace std;
int n, m, k;
int mass[100][100];
int ans = 1000000;
int met[100][100];
int mass1[100][100];
struct coord
{
int x;
int y;
coord()
{
x = 0;
y = 0;
}
coord(int _x, int _y)
{
x = _x;
y = _y;
}
};
void Out(int mass1[100][100])
{
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
cout << mass1[i][j];
cout << endl;
}
cout << endl;
}
int BFS(int x, int y)
{
//массивы смещений относительно клетки
int sx[4] = {-1, 0, 1, 0};
int sy[4] = {0, 1, 0, -1};
//структура данных - ДЕК
coord dec[20011];
int front = 10006;
//указатель на перед
int back = 10005; //указатель на зад
int size = 0;
//размер дека
//======================
int col = 0;
//======================
mass1[x][y] = k; /**/
met[x][y] = 1;
//======================
dec[front] = coord(x, y);//
front++;
//запихиваем в первую дек стартовую клетку
size++;
//==============
while (size > 0)
//пока есть непросмотренные клетки в деке
{
//достаем из дека клетку
front--;
coord u = dec[front];
size--;
2
//==============
for (int i = 0; i < 4; ++i)
{
int x1 = u.x + sx[i];
int y1 = u.y + sy[i];
if (x1 < 0 || x1 >= n || y1 < 0 || y1 >= m)
{
continue;
}
if (mass1[x1][y1] < k)
//если мы ещё тут не были
{
if (mass[x1][y1] == mass[u.x][u.y])//если цвета клеток
совпадают
{
mass1[x1][y1] = mass1[u.x][u.y];
dec[front] = coord(x1, y1);
front++;
size++;
if (mass1[x1][y1] == k)
//
//помещаем в дек вперед
//
//метим клетку как
помеченную
met[x1][y1] = 1;
}
else
{
mass1[x1][y1] = mass1[u.x][u.y] + 1;
dec[back] = coord(x1, y1);//
back--;
//помещаем в дек
size++;
//
}
if (mass1[x1][y1] > col)
{
col = mass1[x1][y1];
}
}
}
}
return col - k;
}
int main()
{
freopen("l.in", "r", stdin);
freopen("l.out", "w", stdout);
//======= Reading ====
cin >> n >> m;
int col = 0;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
{
char ch;
cin >> ch;
mass[i][j] = ch - '0';
3
col+= mass[i][j];
met[i][j] = 0;
mass1[i][j] = 0;
}
//==
частные случаи
====
if (col == 0 || col == n * m)
{
cout << 0;
return 0;
}
if (col > 0 && col < 4)
{
cout << 1;
return 0;
}
if (col > n*m-4 && col < n*m)
{
cout << 1;
return 0;
}
//===========================
k = 1;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (met[i][j] == 0)
{
col = BFS(i, j);
ans = min(ans, col);
k = max(k + col + 1, col + 2);
}
cout << ans;
return 0;
}
4
Download