Техника двух указателей
The Техника двух указателей часто используется в программировании для решения задач, связанных с массивами или последовательностями. Она заключается в использовании двух указателей, которые проходят по массиву с разных начальных позиций, двигаясь в одном или в противоположных направлениях с одной или с разной скоростью. Эта техника особенно полезна для решения задач, связанных с поиском, оптимизацией или обработкой массивов эффективным образом.
Приведем основные этапы работы техники двух указателей:
Инициализация: Устанавливаем начальное положение каждого из двух указателей или на одинаковых, или на разных позициях в массиве.
Перемещение: Попеременно перемещаем указатели на основе определенных условий, пока они не встретятся или не достигнут определенного условия. Перемещение указателей может контролироваться в соответствии с требованиями задачи. Например, один указатель может двигаться быстрее другого, или они могут двигаться в противоположных направлениях.
Проверка условий: На каждом шаге итерации совершается проверка выполнения определенных условий в соответствии с требованиями задачи. Эти условия часто определяют, нужно ли перемещать указатели, обновлять некоторые переменные или выполнять другие операции.
Завершение: Процесс продолжается до тех пор, пока один или оба указателя не достигнут конца массива, или пока они не встретятся, или пока не будет выполнено какое-то другое условие завершения.
Техника двух указателей особенно полезна в задачах, которые включают в себя поиск пар элементов или подмассивов, удовлетворяющих определенным критериям, поиск оптимальных решений или эффективная обработка последовательностей без использования вложенных циклов. Она часто обеспечивает более эффективное решение по сравнению с методами перебора, особенно для задач с требованиями к временной сложности линейного или логарифмического порядка.
Рассмотрим решение задачи с переворотом массива. Для этого воспользуемся техникой двух указателей.
Установим две переменнные и (назовем их указателями):
на начало массива ;
на конец массива ;
Далее запустим while цикл, в котором:
значения и меняются местами;
после чего увеличивается на , а уменьшается на ;
Цикл продолжаем, пока указатели и не встретятся.
Отметим, что поменять значения и можно при помощи дополнительной переменной , выполнив три операции:
Объявим массив.
#define MAX 110 int m[MAX];
Читаем входные данные.
scanf("%d",&n); for(i = 1; i <= n; i++) scanf("%d",&m[i]);
При помощи техники двух указателей переворачиваем массив.
i = 1; j = n; while(i < j) { temp = m[i]; m[i] = m[j]; m[j] = temp; i++; j--; }
Выводим результирующий массив.
for(i = 1; i <= n; i++) printf("%d ",m[i]); printf("\n");
Хусейн с Ярославом играют в карточки. На столе в ряд лежат карточек, на которых написаны различные числа. Игроки ходят по очереди. Первым ходит Хусейн. За один ход игрок может взять либо крайнюю слева карточку, либо крайнюю справа. Игрок всегда выбирает карточку с большим числом. Игра заканчивается, когда на столе не останется карточек. Найдите сумму чисел на карточках Хусейна и Ярослава к концу игры.
Вход. Первая строка содержит количество карточек на столе. Вторая строка содержит натуральных чисел, записанных на карточках. Все числа не превышают .
Выход. Выведите сумму чисел на карточках, которые собрали Хусейн и Ярослав к концу игры.
7 4 7 5 1 12 8 2
18 21
Пусть массив содержит числа, записанные на карточках. Инициализируем указатели на начало массива и на конец массива.
На каждом ходу игрок берет карточку с максимальным значением , после чего карточка убирается со стола (совершаем операцию если выбрана карточка и если выбрана карточка ). Для каждого из игроков подсчитываем сумму выбранных карточек в двух переменных. Процесс продолжается до тех пор, пока все карточки не будут убраны со стола, то есть, пока указатели и не сойдутся.
Пример
Игра будет проходить следующим образом.
Объявим рабочие массивы. Сумму чисел на карточках Хусейна и Ярослава будем подсчитывать соответственно в и .
int m[10001], res[2];
Читаем входные данные.
scanf("%d", &n); for (i = 0; i < n; i++) scanf("%d", &m[i]);
Установим указатели и .
i = 0; j = n - 1;
Игроки последовательно совершают ходов. Ходы для четного совершает Хусейн, а ходы для нечетного совершает Ярослав. Соответственно, сумма Хусейна будет накапливаться в , а сумма Ярослава — в .
for (k = 0; k < n; k++) {
На каждом ходу игрок выбирает карточку с максимальным значением .
if (m[i] > m[j]) res[k % 2] += m[i++]; else res[k % 2] += m[j--]; ; }
Выводим ответ.
printf("%d %d\n", res[0], res[1]);
Хусейн разложил в ряд карточек с числами . Затем он собрал их и разложил в другом порядке: . Именно эту последовательность он и передал Ярославу. Помогите Ярославу восстановить исходную последовательность Хусейна.
Например, если Ярослав получил последовательность , то Хусейну он должен вернуть последовательность .
Вход. Первая строка содержит число — количество карточек. Вторая строка содержит натуральных чисел, записанных на карточках. Все числа не превышают .
Выход. Выведите исходную последовательность Хусейна.
6 2 4 9 4 7 6
2 6 4 7 9 4
7 5 7 34 1 89 4 2
5 2 7 4 34 89 1
Пусть массив содержит числа, записанные на карточках. Инициализируем два указателя: на начало массива и на его конец.
Для восстановления исходной последовательности следует попеременно брать карточки то слева, то справа, пока не будут обработаны все элементы. При каждом выборе соответствующий указатель сдвигается: — вправо, — влево.
Пример
Рассмотрим несколько шагов работы алгоритма для первого теста.
Объявим рабочий массив.
int a[100001];
Читаем входные данные.
scanf("%d", &n); for (i = 0; i < n; i++) scanf("%d", &a[i]);
Установим указатели и .
i = 0; j = n - 1;
Пока выполняется неравенство , попеременно выводим то то , одновременно передвигая указатели.
while (i <= j) { printf("%d ", a[i++]); if (i > j) break; printf("%d ", a[j--]); }
У Хусейна есть строка, состоящая из символов и . Чтобы развлечься, он придумал следующую игру. Хусейн может выполнять одну из двух операций над строкой:
приписать с левого конца , а с правого ;
приписать с правого конца , а с левого ;
Например, из строки можно получить или .
Вам дана строка, полученная после выполнения всех операций (возможно, Хусейн не сделал ни одной операции). Определите, какой могла быть наименьшая возможная длина строки до начала изменений.
Вход. Одна строка длины не более , содержащая только символы и .
Выход. Выведите наименьшую возможную длину строки, которая изначально могла быть у Хусейна.
01010010
8
1001110
1
Рассмотрим входную строку — результирующую строку Хусейна после выполнения всех операций. Инициализируем два указателя: на начало и на конец строки.
Если , то на концах строки находятся разные символы. А именно или слева и справа, или слева и справа. Это значит, что предыдущая строка могла быть увеличена в результате применения одной из заданных операций. В этом случае сдвигаем оба указателя и на одну позицию навстречу друг другу и снова проверяем, могла ли подстрока быть получена в результате операций Хусейна.
Как только для текущей подстроки будет выполняться , ее уже нельзя получить при помощи данных операций. Выводим ее длину – это и будет исходная строка Хусейна.
Пример
Проведем операции Хусейна в обратном порядке.
Строка изначально была у Хусейна.
Читаем входную строку. Вычисляем ее длину в переменной .
cin >> s; res = s.size();
Инициализируем два указателя: на начало и на конец строки.
i = 0; j = s.size() - 1;
Если , то на концах строки находятся разные символы. Хусейн мог при помощи своей операции получить строку из строки .
while ((i < j) && (s[i] != s[j])) {
Сдвигаем оба указателя и на одну позицию навстречу друг другу и уменьшаем на текущий размер подстроки .
i++; j--; res -= 2; }
Выводим ответ.
cout << res << endl;
Задан массив , отсортированный по возрастанию и содержащий целых чисел. Определите, существует ли в нем такая пара чисел , сумма которых равна .
Вход. Первая строка содержит два целых числа и . Вторая строка содержит целых неотрицательных чисел, каждое из которых не больше .
Выход. Выведите "YES" если такая пара элементов существует, и "NO" иначе.
10 13 1 3 5 6 8 10 11 11 11 16
YES
8 61 5 5 8 12 16 21 44 50
NO
Пусть — входной массив чисел. Инициализируем указатели на начало массива, на конец массива. Массив по условию задачи уже отсортирован.
Пока указатели и не встретятся, совершаем следующие действия:
Если , то искомая пара элементов найдена, выводим ее и завершаем программу.
Если , то двигаем указатель на одну позицию вправо. В этом случае сумма будет увеличиваться;
Если , то двигаем указатель на одну позицию влево. В этом случае сумма будет уменьшаться;
Пример
Рассмотрим первый тест. Инициализируем указатели. Промоделируем работу алгоритма. Значение , ищем два числа с суммой .
Читаем входные данные.
scanf("%d %d", &n, &x); v.resize(n); for (i = 0; i < n; i++) scanf("%d", &v[i]);
Инициализируем указатели.
int i = 0, j = v.size() - 1;
Указатель двигаем вперед, указатель двигаем назад.
while (i < j) {
Если , то искомая пара элементов найдена. Выводим ее и завершаем программу.
if (v[i] + v[j] == x) { printf("YES\n"); return 0; }
Если , то двигаем указатель на одну позицию вправо;
Если , то двигаем указатель на одну позицию влево;
if (v[i] + v[j] < x) i++; else j--; }
Если искомая пара не найдена, то выводим "NO".
printf("NO\n");
Задан массив целых чисел и целое число . Найдите такую тройку чисел в массиве, сумма которых равна . Все индексы должны быть различны.
Вход. Первая строка содержит размер массива и значение . Вторая строка содержит целых чисел, каждое из которых по модулю не больше .
Выход. Если требуемая тройка чисел существует, то выведите ее в любом порядке. Если существует несколько троек, выведите любую. Если искомой тройки не существует, выведите .
8 19 20 3 5 1 15 7 17 12
1 3 15
Пусть — входной массив. Переберем все возможные индексы . Далее для каждого значения найдем такую пару индексов что и . Это можно сделать на отсортированном массиве с помощью техники двух указателей за линейное время.
Читаем входные данные.
cin >> n >> x; v.resize(n); for (i = 0; i < n; i++) cin >> v[i];
Сортируем входной массив.
sort(v.begin(), v.end());
Перебираем значения .
for (k = 0; k < n - 2; k++) {
Ищем такую пару индексов , что и . Инициализируем индексы и .
i = k + 1; j = n - 1; s = x - v[k];
Используем технику двух указателей для поиска искомой пары .
while (i < j) { if (v[i] + v[j] == s) {
Если пара найдена, то выводим искомую тройку чисел .
printf("%d %d %d\n", v[k], v[i], v[j]); return 0; }
Если , то двигаем указатель на одну позицию вправо;
Если , то двигаем указатель на одну позицию влево;
if (v[i] + v[j] < s) i++; else j--; } }
Если тройка чисел не найдена, то выводим .
cout << -1 << endl;
Задан отсортированный массив из целых чисел. Для каждого индекса найдите, сколько элементов в массиве лежит между и включительно.
Вход. Первая строка содержит размер массива . Вторая строка содержит целых чисел в диапазоне от до в отсортированном порядке.
Выход. Выведите чисел. Для каждого индекса массива выведите количество элементов, лежащих между и включительно.
10 1 2 3 4 5 6 7 8 9 10
2 3 4 5 6 5 4 3 2 1
Интервал будем называть хорошим, если на нем находятся числа в промежутке включительно (то есть ).
Реализуем скользящее окно при помощи двух указателей и и будем его поддерживать так, чтобы текущий рассматриваемый интервал был хорошим, а интервал плохим.
Например, для следующей последовательности
интервалы и являются хорошими.
интервалы являются плохими.
Если , то расширяем текущий интервал до . Иначе сокращаем его до . Перед передвижением указателя выводим количество элементов, лежащих между и включительно. Оно равно .
Оценка сложности алгоритма линейная, то есть .
Пример
Рассмотрим движение указателей для приведенного примера.
Для заданного состояния , поэтому двигаем вперед указатель .
Теперь . Вперед следует двигать указатель . Однако перед его перемещением выводим количество элементов, лежащих между и включительно. Оно равно .
Двигаем вперед пока .
Теперь . Выводим ответ для (он равен ) и увеличиваем на .
Читаем входные данные.
scanf("%d", &n); for (i = 0; i < n; i++) scanf("%d", &a[i]);
Инициализируем указатели .
i = j = 0;
Двигаем указатели вперед пока для каждого значения не будет найден ответ.
while (i < n) // [i..j] {
Если (но при этом не вышло за границы массива, то есть ), то расширяем текущий интервал, увеличивая указатель .
if ((j < n) && (a[j] <= 2 * a[i])) j++; else {
При выводим ответ для индекса и увеличиваем на единицу.
printf("%d ", j - i); i++; } }
Вдоль прекрасного Адриатического побережья расположено отелей. Каждый отель имеет свою стоимость в евро.
Петр выиграл евро в лотерею. Теперь он хочет купить последовательность следующих друг за другом отелей так, чтобы сумма стоимостей этих последовательных отелей была как можно больше, но не превышала .
Вы должны рассчитать эту максимально возможную общую стоимость.
Вход. В первой строке заданы два целых числа и . В следующей строке заданы натуральных чисел меньших , представляющих стоимости отелей в том порядке, в котором они расположены вдоль побережья.
Выход. Выведите искомую максимальную стоимость (оно будет больше во всех тестах).
5 12 2 1 3 4 5
12
Стоимости отелей занесем в массив . Интервал будем называть хорошим, если сумма стоимостей отелей на нем не больше .
Реализуем скользящее окно при помощи двух индексов и и будем его поддерживать так, чтобы текущий рассматриваемый интервал был хорошим. Пусть — сумма стоимостей отелей на интервале . Если , то расширяем текущий интервал до . Иначе сокращаем его до . Находим максимум среди стоимостей всех рассматриваемых хороших интервалов.
Пример
Рассмотрим движение указателей для приведенного примера.
движется вперед пока сумма чисел на интервале не больше . Остановимся на интервале , так как на нем сумма равна , а на интервале сумма равна .
подвинуть вперед не можем так как сумма на интервале равна .
двигаем вперед до интервала .
Максимальное значение среди всех хороших интервалов равно и достигается на интервале .
Читаем входные данные.
scanf("%d %lld", &n, &m);
Инициализируем переменные:
в вычисляется максимальная стоимость среди стоимостей всех обрабатываемых хороших интервалов;
в поддерживается сумма чисел на текущем интервале . Все числа текущего интервала содержатся в очереди ;
s = res = 0;
Последовательно обрабатываем стоимости отелей.
for (i = 0; i < n; i++) {
Читаем и добавляем стоимость текущего отеля в очередь.
scanf("%d", &val); q.push(val);
Обновляем сумму на интервале. Все элементы текущего интервала находятся в очереди .
s += val;
Если сумма чисел на текущем интервале больше , то удаляем элементы с начала интервала.
while (s > m) { s -= q.front(); q.pop(); }
Вычисляем максимальное значение среди сумм элементов допустимых интервалов (стоимость отелей на которых не превышает ).
if (s > res) res = s; }
Выводим ответ.
printf("%lld\n", res);
Дан массив из положительных целых чисел. Найдите количество подмассивов, сумма которых равна .
Вход. Первая строка содержит размер массива и заданную сумму . Следующая строка содержит целых чисел — элементы массива.
Выход. Выведите требуемое количество подмассивов.
5 7 2 4 1 2 7
3
Реализуем скользящее окно с помощью двух указателей и . Для каждого фиксированного будем по максимуму раздвигать интервал так, чтобы сумма элементов на нем была не больше . То есть для каждого ищем такое наибольшее значение , что сумма элементов на интервале не превышает , а сумма элементов на интервале уже больше .
Пусть — сумма чисел на интервале . Если , то расширяем интервал до . Иначе сокращаем его до . Подсчитываем количество интервалов с суммой .
Пример
Рассмотрим движение указателей для приведенного примера.
, движется вперед, пока сумма чисел на интервале не станет больше . Остановимся на интервале , так как сумма чисел на нем равна , а на интервале сумма чисел уже равна .
, двигаем вперед до . Сумма чисел на интервале равна , а на интервале уже .
, рассматриваемый интервал . Сумма чисел на нем равна , однако двигать индекс вперед мы не можем, потому что сумма чисел на интервале равна , что больше .
, рассматриваемый интервал . Сумма чисел на нем равна , однако двигать индекс вперед мы не можем, потому что сумма чисел на интервале равна , что больше .
, рассматриваемый интервал . Сумма чисел на нем равна .
Количество подмассивов с суммой равно . Они начинаются с индексов и .
Объявим рабочий массив.
#define MAX 200001 long long a[MAX];
Читаем входные данные.
scanf("%d %lld", &n, &x); for (i = 0; i < n; i++) scanf("%lld", &a[i]);
Изначально установим текущий интервал . Сумма чисел на этом интервале равна .
s = j = 0;
Для каждого значения последовательно обрабатываем интервалы .
for (i = 0; i < n; i++) {
Для фиксированного левого конца интервала ищем наибольшее , для которого сумма элементов на указанном интервале не превосходит .
while ((j < n) && (s + a[j] <= x)) s += a[j++];
Если сумма чисел на текущем интервале равна , то искомое количество подмассивов увеличиваем на .
if (s == x) cnt++;
Пересчитываем сумму чисел для интервала .
s -= a[i]; }
Выводим ответ.
printf("%lld\n", cnt);
Завтра Зия примет участие в соревновании последовательностей. Число называется вершиной некоторой последовательности, если последовательность является подпоследовательностью данной последовательности. Силой каждой последовательности считается ее наибольшая вершина.
Завтра все студенты пойдут на соревнование и победителем станет обладатель сильнейшей последовательности. Зия имеет последовательность . Он хочет завладеть системой оценки соревнования и удалить из нее последовательности с большей силой, чем у него. Однако Зия не знает силу собственной последовательности, но очень хочет победить. Помогите ему определить силу собственной последовательности.
Вход. В первой строке задано количество чисел в последовательности Зии. В следующей строке записаны целых чисел — элементы последовательности.
Выход. Выведите одно число — силу данной последовательности.
2 2 10
0
3 1 2 3
1
5 1 10 2 3 1
2
Пусть — входной массив чисел. Инициализируем указатели на начало массива и на конец массива. В переменной будем подсчитывать силу последовательности. Изначально установим .
Пока указатели и не встретятся, выполняем следующие действия:
Сдвигаем указатель на одну позицию вправо, если он не указывает на число ;
Сдвигаем указатель на одну позицию влево, если он не указывает на число ;
Если оба указателя указывают на число , увеличиваем на единицу и сдвигаем каждый из указателей на одну позицию в соответствующем направлении;
После завершения работы алгоритма ответом будет значение .
Пример
Рассмотрим второй тест. Инициализируем указатели. Двигаем указатель вправо, а влево пока они не будут указывать на число .
Двигаем указатель вправо, а влево, пока они не будут указывать на число .
Указатели встретились, останавливаем алгоритм. Ответом будет значение .
Читаем входные данные.
scanf("%d", &n); v.resize(n); for (i = 0; i < n; i++) scanf("%d", &v[i]);
Установим указатели на начало и конец массива .
int i = 0, j = v.size() - 1;
В переменной подсчитываем силу последовательности.
k = 1; while (i <= j) {
Указатель двигаем вправо пока не встретится число .
if (v[i] != k) i++;
Указатель двигаем влево пока не встретится число .
if (v[j] != k) j--;
Если оба указателя указывают на число , то увеличиваем на единицу.
if (v[i] == k && v[j] == k) k++; }
Выводим ответ.
printf("%d\n", k - 1);
Сегодня собрались n программистов. У каждого программиста есть рейтинг, показывающий его силу. Рейтинг — это целое число от до . Ваш рейтинг как программиста равен . Со всех собравшихся сегодня программистов Вы хотите выбрать двух в свою команду. Их необходимо выбрать таким образом, чтобы сумма их рейтингов была максимальной, но не превышала Ваш рейтинг, поскольку Вы хотите быть лидером этой команды.
Вход. В первой строке заданы два целых числа: — количество программистов и — Ваш рейтинг. Во второй строке приведены целых чисел — рейтинги программистов.
Выход. Выведите одно целое число — максимальную сумму рейтингов выбранных программистов или , если таких двух человек найти невозможно.
5 8 5 3 4 6 5
8
7 19 8 4 25 1 20 5 12
17
4 76 38 41 39 40
-1
Отсортируем рейтинги программистов в массиве . Поиск двух программистов с максимальным суммарным рейтингом будем осуществлять при помощи техники двух указателей.
Инициализируем указатель на начало массива и указатель на конец массива.
Среди всех возможных сумм , не превышающих , находим максимальную. Если , сдвигаем указатель вперед. В противном случае сдвигаем указатель назад. Процесс передвижения указателей продолжаем до тех пор, пока они не встретятся.
Пример
Рассмотрим второй тест. Отсортируем числа и инициализируем указатели. Промоделируем работу алгоритма. В данном случае : ищем два числа с максимальной суммой, не превышающей .
Читаем входные данные.
scanf("%d %d", &n, &m); v.resize(n); for (i = 0; i < n; i++) scanf("%d", &v[i]);
Сортируем рейтинги программистов.
sort(v.begin(), v.end());
Инициализируем указатели. В переменной вычисляем максимальную сумму двух элементов.
int mx = -1, low = 0, high = n - 1;
Указатель двигаем вперед, указатель двигаем назад.
while (low < high) {
Среди всех возможных сумм , не больших , находим максимум. Если , то двигаем вперед . Иначе двигаем назад .
if (v[low] + v[high] <= m) { mx = max(mx, v[low] + v[high]); low++; } else high--; }
Выводим ответ.
printf("%d\n", mx);
Корова Бесси, всегда увлекающаяся блестящими предметами, занялась добычей алмазов в свободное время! Она собрала алмазов разных размеров и хочет разместить некоторые из них в витрине в амбаре.
Поскольку Бесси хочет, чтобы алмазы в витрине были относительно похожи по размеру, она решила, что не будет включать в витрину два алмаза, если их размеры различаются более чем на (два алмаза могут быть выставлены вместе, если их размеры отличаются ровно на ). Зная значение , помогите Бесси определить максимальное количество алмазов, которые она сможет выставить в витрине.
Вход. Первая строка содержит и . Каждая из следующих строк содержит одно целое число — размер одного алмаза. Все размеры — положительные числа, не превышающие .
Выход. Выведите максимальное количество алмазов, которое Беси сможет продемонстрировать в витрине амбара.
5 3 1 6 4 3 1
4
Отсортируем массив с размерами алмазов. Для каждого индекса рассмотрим последовательность такую, что , но при этом . Среди всех таких последовательностей найдем ту, которая содержит наибольшее количество элементов.
Пример
Отсортируем размеры алмазов.
Рассмотрим интервал . Разница между размерами алмазов на нем не превышает . Однако разместить в амбаре все алмазы в интервале Бесси уже не сможет, так как .
Объявим массив для хранения размеров алмазов.
int m[1000];
Читаем входные данные.
scanf("%d %d", &n, &k); for (i = 0; i < n; i++) scanf("%d", &m[i]);
Отсортируем массив.
sort(m, m + n);
Максимальное количество алмазов, которое Беси сможет разместить в амбаре, подсчитываем в переменной .
res = 0;
Для каждого индекса рассматриваем последовательность наибольшей длины, такую, что . Длину этой последовательности подсчитываем в переменной .
for (i = 0; i < n; i++) { cnt = 0; for (j = i; j < n; j++) {
Как только условие становится истинным, выходим из цикла.
if (m[j] > m[i] + k) break; cnt++; }
Среди длин всех последовательностей находим наибольшую.
if (cnt > res) res = cnt; }
Выводим ответ.
printf("%d\n", res);
Имеется массив целых чисел длины , задающий высоты n вертикальных линий. Для каждой -й линии её конечные точки заданы координатами и .
Найдите две такие линии, которые вместе с осью образуют контейнер, способный удержать максимальное количество воды.
Вход. Первая строка содержит размер массива . Вторая строка содержит натуральных чисел — элементы массива , каждое из которых не превышает .
Выход. Выведите максимальный объём воды, который может удержать контейнер.
9 1 8 6 2 5 4 8 3 7
49
Сначала вычислим объем воды в контейнере между крайними верткальными линиями, то есть между линиями пары . Затем будем двигать указатели и навстречу друг другу:
если , то увеличим на ;
если , то уменьшим на ;
Объем воды между линиями и равен . Среди всех полученных значений находим наибольшее.
Задачу можно решить за . Однако из-за ограничения такое решение приведёт к превышению лимита времени (Time Limit). Достаточно перебрать пары линий , для которых , и далее для каждой такой пары вычислить объем воды, который они могут удерживать. Среди всех вычисленных значений находим максимальный объём.
Пример
Контейнер из первого теста имеет вид:
Наибольшее количество воды можно удержать между линиями и . Высота уровня воды составит , а объем воды равен .
Читаем входные данные.
scanf("%d", &n); h.resize(n); for (i = 0; i < n; i++) scanf("%lld", &h[i]);
В переменной будем хранить максимальное количество воды, которое может удерживать контейнер.
long long res = 0;
Инициализируем пару указателей .
int l = 0, r = h.size() - 1;
Двигаем указатели и навстречу друг другу, пока они не встретятся.
while (l < r) {
Объем воды между линиями и равен . Среди всех таких объемов находим максимальный.
res = max(res, min(h[l], h[r]) * (r - l));
Перемещаем указатели.
if (h[l] < h[r]) l += 1; else r -= 1; }
Выводим ответ.
printf("%lld\n", res);
Вам дан список песен, которые играли на радио 106 FM. Всего в списке содержится песен. Найдите длину самого длинного фрагмента списка, состоящего из неповторяющихся песен.
Вход. Первая строка содержит количество песен в списке. Вторая строка содержит целых чисел — идентификационные номера песен.
Выход. Выведите длину самого длинного фрагмента списка, состоящего из неповторяющихся песен.
8 1 2 1 3 2 7 4 2
5
4 1 1 2 1
2
Пусть массив хранит идентификационные номера песен. С помощью двух указателей будем поддерживать скользящее окно , в котором песни не повторяются. Иными словами, на отрезке все песни уникальны. Песни из этого отрезка будем хранить во множестве . Для каждого значения будем стремиться расширить отрезок до максимальной возможной длины. То есть для фиксированного будем искать такое , что на отрезке песни не повторяются, а на отрезке уже появляются дубли.
При обработке текущей песни с индексом имеются два варианта:
если песня не встречается в отрезке , то добавляем ее в этот отрезок, расширяя его до ;
если песня уже присутствует в отрезке, сдвигаем левую границу вправо до тех пор, пока не исчезнет из отрезка . Затем добавляем в отрезок, расширяя его до . При каждом сдвиге удаляем соответствующие элементы из множества ;
Среди всех возможных отрезков с уникальными песнями находим максимальную длину. Это и будет ответом на задачу.
Пример
Рассмотрим как будут изменяться отрезки с неповторяющимися песнями для первого примера.
Длина наибольшего отрезка равна .
Читаем входные данные. Идентификационные номера песен храним в массиве .
scanf("%d", &n); v.resize(n); for (i = 0; i < n; i++) scanf("%d", &v[i]);
Поддерживаем отрезок из неповторяющихся песен (то есть песни от до не повторяются). Во множестве храним идентификационные номера песен из этого отрезка. В переменной отслеживаем длину самого длинного фрагмента с уникальными песнями.
i = res = 0; for (j = 0; j < n; j++) {
Рассматриваем текущую песню с индексом . Если она уже содержится во множестве , то расширить текущий отрезок за счет -ой песни не удастся. В этом случае необходимо сдвигать левую границу , удаляя из множества песню , до тех пор, пока -ая песня не исчезнет из множества .
while (s.find(v[j]) != s.end()) { s.erase(v[i]); i++; }
Добавим -ую песню в отрезок и, соответственно, во множество . Таким образом, отрезок не будет содержать повторяющихся песен.
s.insert(v[j]);
Среди всех возможных отрезков находим самый длинный. Длина отрезка равна .
res = max(res, j - i + 1); }
Выводим ответ.
printf("%d\n", res);