Разбор задач контеста 30.11 01-02. Это задачи на несложную

advertisement
Разбор задач контеста 30.11
01-02.
Это задачи на несложную технику программирования и
владение языком: в обоих задачах нужно уметь работать со
вложенными друг в друга циклами, а в задаче 02 – еще и с
массивами. В разборе не нуждаются.
03. Скобки.
Открывающаяся скобка в позиции i является парной к
закрывающейся скобке в позиции j (i < j), если часть скобочной
последовательности, записанная между i и j, является правильной.
Проверить, что скобочная последовательность является
правильной, можно так: будем идти по последовательности и
считать баланс – разность между количеством встреченных
открывающихся и закрывающихся скобок. Последовательность
правильная, если в итоге баланс равен 0, и никогда не становился
меньше 0.
Альтернативное решение: каждый раз брать самую правую
открывающуюся скобку, для которой нет пары. Тогда для нее
парной является ближайшая к ней справа закрывающаяся скобка, у
которой пока нет пары.
Реализация этой идеи:
uses
SysUtils;
var
i, j : Integer;
s : string;
a : array[1..257] of integer;
begin
readln(s);
for i := 1 to length(s) do
a[i] := 0;
for i := length(s) downto 1 do
begin
if (s[i] = '(') then
for j := i + 1 to length(s) do
if (s[j] = ')') and (a[j] = 0) then
begin
a[j] := i;
a[i] := j;
Break;
end;
end;
for i := 1 to length(s) do
write(a[i], ' ');
end.
04. Лыжник
Будем считать, что лыжник находится на координатной плоскости и
вначале стоит в точке (0, 0). Тогда каждое его движение – это
изменение одной из координат на единицу. Сохраним уже
пройденный путь как множество отрезков (в массиве). Теперь,
когда нам на текущем шаге нужно определить, был ли посещен
текущий отрезок, достаточно проверить его на совпадение со
всеми сохраненными отрезками.
Альтернативное решение: заметим, что каждый отрезок можно
однозначно охарактеризовать координатами его середины. Для
того, чтобы координаты середин отрезков являлись целыми
числами, домножим все координаты на 2 (то есть теперь лыжник
ходит не на 1 единицу длины, а на 2). Теперь в двумерном массиве
a[-200..200, -200..200] типа Boolean мы можем каждую уже
пройденную середину отрезка помечать значением true.
Код решения, основанный на такой идее:
type int=integer;
const n=300; way:set of char=['N','E','W','S'];
var a:array[-n..n,-n..n] of boolean;
i,j,s:int;
c:char;
Begin
fillchar(a,sizeof(a),0);
i:=0; j:=0; s:=0;
read(c);
while c in way do
begin
case c of
'N':inc(j);
'W':dec(i);
'S':dec(j);
'E':inc(i);
end;
if a[i,j] then inc(s) else inc(s,5);
a[i,j]:=true;
case c of
'N':inc(j);
'W':dec(i);
'S':dec(j);
'E':inc(i);
end;
a[i,j]:=true;
read(c);
end;
write(s);
End.
05. Соревнование
Понятно, что для того, чтобы решить как можно больше задач, Пете
надо из всех доступных для решения, но еще не решенных задач,
выбирать ту, на которую он потратит меньше всего времени.
Используем для быстрого нахождения нужной задачи структуру
данных, в которая эффективно поддерживает вставку элемента,
нахождение минимума и удаление минимума. В качестве такой
структуры нам подойдет двоичная куча, дерево отрезков, или же
просто стандартные std::set в C++ или TreeSet в Java. Причем, в
структуре данных будут храниться доступные на данный момент
времени для решения задач. С увеличением числа задач,
решенных Петей, в структуру добавляются новые задачи. Прибавив
время, потраченное на решение очередной задачи, к счетчику
времени, допишем номер этой задачи в массив ответов и удалим
ее из структуры данных. Это будем повторять до тех пор, пока
счетчик времени не станет больше K или пока не будут решены все
задачи.
06. Долина Золотоискателей
Научимся решать эту задачу для фиксированной длины
стороны участка m за время O(N²).
Очевидно, что можно рассматривать только квадраты,
содержащие месторождения на нижней и левой границах. Если у
нас не такой квадрат, то возьмём и подвинем его сначала вверх до
тех пор, пока какое-либо месторождение не окажется на его
нижней границе, а затем точно так же подвинем его вправо.
Месторождения, которые были в участке до сдвигов, из него
не исчезнут, могут лишь добавиться новые, что не ухудшит
результат.
Отсортируем месторождения по возрастанию координаты X и
переберём их все по очереди. Второй координатой текущего
месторождения будет Y. Рассмотрим множество всех квадратов, на
нижней границе которых находится текущее месторождение. Для
них нижней границей будет координата Y, а верхней границей - (Y
+ m). Теперь за время O(N) можно «оценить» всевозможные такие
квадраты, выбирая по очереди самое левое месторождение.
Теперь рассмотрим задачу в общем случае. Отметим
свойство: если на участке со стороной t суммарный доход не
меньше W, то для любого r > t можно найти квадрат со стороной r,
удовлетворяющий поставленному условию. Поэтому мы можем
воспользоваться бинарным поиском для нахождения сначала
наименьшей возможной стороны квадрата, а затем и самого
квадрата за время O(N ²log m), где m – максимальная разница по X
и по Y между всеми парами различных месторождений.
program GoldenseekersValley;
uses
SysUtils;
type
site = record
x, y, money: integer;
end;
var
a: array[0..501] of site;
n, i, j, l, r, m, p, w, sum, bestl, bestm, bestd,
low, high: integer;
success: boolean;
begin
read(n, w);
for i:=1 to n do
begin
read(a[i].x, a[i].y, a[i].money);
dec(a[i].y);
end;
for i:=1 to n do
for j:=i + 1 to n do
if a[i].x > a[j].x then
begin
a[0]:=a[i];
end;
a[0].x:=0;
a[i]:=a[j];
a[j]:=a[0];
a[0].y:=0;
a[0].money:=0;
bestm:= - 1;
bestl:= - 1;
bestd:= - 1;
l:=1;
r:=50000;
while l < r do
begin
m:=(l + r) div 2;
success:=false;
for i:=1 to n do
begin
low:=a[i].y;
high:=a[i].y + m - 1;
p:=0;
sum:=0;
for j:=1 to n do
begin
if (a[j].y >= low) and (a[j].y <= high)
then inc(sum, a[j].money);
while a[p].x + m <= a[j].x do
begin
if (a[p].y >= low) and (a[p].y <=
high) then dec(sum, a[p].money);
inc(p);
end;
if (sum >= w) and not success then
begin
bestl:=a[p].x;
bestd:=a[i].y;
bestm:=m;
success:=true;
end;
end;
end;
if success then r:=m
else l:=m + 1;
end;
if bestm = - 1 then write('NO SOLUTION')
else write(bestl, ' ', bestd + bestm, ' ', bestl
+ bestm, ' ', bestd);
end.
Download