Basecamp
    Главная
    Задачи
    Соревнования
    Курсы
    Рейтинг
    Посты
    Store
    Discord
Техника двух указателей
Войти
medv
•Статьи•11 месяцев назад

Техника двух указателей

Техника двух указателей часто используется в программировании для решения задач, связанных с массивами или последовательностями. Она заключается в использовании двух указателей, которые проходят по массиву с разных начальных позиций, двигаясь в одном или в противоположных направлениях с одной или с разной скоростью. Эта техника особенно полезна для решения задач, связанных с поиском, оптимизацией или обработкой массивов эффективным образом.

Приведем основные этапы работы техники двух указателей:

  • Инициализация: Устанавливаем начальное положение каждого из двух указателей или на одинаковых, или на разных позициях в массиве.

  • Перемещение: Попеременно перемещаем указатели на основе определенных условий, пока они не встретятся или не достигнут определенного условия. Перемещение указателей может контролироваться в соответствии с требованиями задачи. Например, один указатель может двигаться быстрее другого, или они могут двигаться в противоположных направлениях.

  • Проверка условий: На каждом шаге итерации совершается проверка выполнения определенных условий в соответствии с требованиями задачи. Эти условия часто определяют, нужно ли перемещать указатели, обновлять некоторые переменные или выполнять другие операции.

  • Завершение: Процесс продолжается до тех пор, пока один или оба указателя не достигнут конца массива, или пока они не встретятся, или пока не будет выполнено какое-то другое условие завершения.

Техника двух указателей особенно полезна в задачах, которые включают в себя поиск пар элементов или подмассивов, удовлетворяющих определенным критериям, поиск оптимальных решений или эффективная обработка последовательностей без использования вложенных циклов. Она часто обеспечивает более эффективное решение по сравнению с методами перебора, особенно для задач с требованиями к временной сложности линейного или логарифмического порядка.

Eolymp #2098. Переворачиватель

Заданы n чисел. Выведите их в обратном порядке.

Вход. Сначала задано число n (0<n<100), за ним идут n целых чисел.

Выход. Выведите заданные n чисел в обратном порядке.

Пример входа
7
2 4 1 3 5 3 1
Пример выхода
1 3 5 3 1 4 2
Открыть задачу
Решение

Рассмотрим решение задачи с переворотом массива. Для этого воспользуемся техникой двух указателей.

Установим две переменнные i и j (назовем их указателями):

  • i на начало массива (i=1);

  • j на конец массива (j=n);

Далее запустим while цикл, в котором:

  • значения m[i] и m[j] меняются местами;

  • после чего i увеличивается на 1, а j уменьшается на 1;

Цикл продолжаем, пока указатели i и j не встретятся.

Отметим, что поменять значения m[i] и m[j] можно при помощи дополнительной переменной temp, выполнив три операции:

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

Объявим массив.

#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");
Eolymp #11387. Игра в карточки

Хусейн с Ярославом играют в карточки. На столе в ряд лежат n карточек, на которых написаны различные числа. Игроки ходят по очереди. Первым ходит Хусейн. За один ход игрок может взять либо крайнюю слева карточку, либо крайнюю справа. Игрок всегда выбирает карточку с большим числом. Игра заканчивается, когда на столе не останется карточек. Найдите сумму чисел на карточках Хусейна и Ярослава к концу игры.

Вход. Первая строка содержит количество карточек n (1≤n≤10000) на столе. Вторая строка содержит n натуральных чисел, записанных на карточках. Все числа не превышают 109.

Выход. Выведите сумму чисел на карточках, которые собрали Хусейн и Ярослав к концу игры.

Пример входа
7
4 7 5 1 12 8 2
Пример выхода
18 21
Открыть задачу
Решение

Пусть массив m содержит числа, записанные на карточках. Инициализируем указатели i=0 на начало массива и j=n−1 на конец массива.

На каждом ходу игрок берет карточку с максимальным значением max(m[i],m[j]), после чего карточка убирается со стола (совершаем операцию i++ если выбрана карточка m[i] и j−− если выбрана карточка m[j]). Для каждого из игроков подсчитываем сумму выбранных карточек в двух переменных. Процесс продолжается до тех пор, пока все карточки не будут убраны со стола, то есть, пока указатели i и j не сойдутся.

Пример

Игра будет проходить следующим образом.

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

Объявим рабочие массивы. Сумму чисел на карточках Хусейна и Ярослава будем подсчитывать соответственно в res[0] и res[1].

int m[10001], res[2];

Читаем входные данные.

scanf("%d", &n);
for (i = 0; i < n; i++)
  scanf("%d", &m[i]);

Установим указатели i=0 и j=n−1.

i = 0; j = n - 1;

Игроки последовательно совершают n ходов. Ходы для четного k совершает Хусейн, а ходы для нечетного k совершает Ярослав. Соответственно, сумма Хусейна будет накапливаться в res[0], а сумма Ярослава — в res[1].

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

На каждом ходу игрок выбирает карточку с максимальным значением max(m[i],m[j]).

  if (m[i] > m[j])
    res[k % 2] += m[i++];
  else
    res[k % 2] += m[j--]; ;
}

Выводим ответ.

printf("%d %d\n", res[0], res[1]);
Eolymp #11388. Игра в карточки 2

Хусейн разложил в ряд n карточек с числами a1​,a2​,a3​,...,an​. Затем он собрал их и разложил в другом порядке: a1​,a3​,a5​,...,a6​,a4​,a2​. Именно эту последовательность он и передал Ярославу. Помогите Ярославу восстановить исходную последовательность Хусейна.

Например, если Ярослав получил последовательность (2,4,9,4,7,6), то Хусейну он должен вернуть последовательность (2,6,4,7,9,4).

Вход. Первая строка содержит число n (1≤n≤10000) — количество карточек. Вторая строка содержит n натуральных чисел, записанных на карточках. Все числа не превышают 109.

Выход. Выведите исходную последовательность Хусейна.

Пример входа 1
6
2 4 9 4 7 6
Пример выхода 1
2 6 4 7 9 4
Пример входа 2
7
5 7 34 1 89 4 2
Пример выхода 2
5 2 7 4 34 89 1
Открыть задачу
Решение

Пусть массив a содержит числа, записанные на карточках. Инициализируем два указателя: i=0 на начало массива и j=n−1 на его конец.

Для восстановления исходной последовательности следует попеременно брать карточки то слева, то справа, пока не будут обработаны все элементы. При каждом выборе соответствующий указатель сдвигается: i — вправо, j — влево.

Пример

Рассмотрим несколько шагов работы алгоритма для первого теста.

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

Объявим рабочий массив.

int a[100001];

Читаем входные данные.

scanf("%d", &n);
for (i = 0; i < n; i++)
  scanf("%d", &a[i]);

Установим указатели i=0 и j=n−1.

i = 0; j = n - 1;

Пока выполняется неравенство i≤j, попеременно выводим то ai​ то aj​, одновременно передвигая указатели.

while (i <= j)
{
  printf("%d ", a[i++]);
  if (i > j) break;
  printf("%d ", a[j--]);
}
Eolymp #11389. Игра Хусейна

У Хусейна есть строка, состоящая из символов 0 и 1. Чтобы развлечься, он придумал следующую игру. Хусейн может выполнять одну из двух операций над строкой:

  • приписать с левого конца 0, а с правого 1;

  • приписать с правого конца 0, а с левого 1;

Например, из строки 010 можно получить 00101 или 10100.

Вам дана строка, полученная после выполнения всех операций (возможно, Хусейн не сделал ни одной операции). Определите, какой могла быть наименьшая возможная длина строки до начала изменений.

Вход. Одна строка длины не более 105, содержащая только символы 0 и 1.

Выход. Выведите наименьшую возможную длину строки, которая изначально могла быть у Хусейна.

Пример входа 1
01010010
Пример выхода 1
8
Пример входа 2
1001110
Пример выхода 2
1
Открыть задачу
Решение

Рассмотрим входную строку s — результирующую строку Хусейна после выполнения всех операций. Инициализируем два указателя: i=0 на начало и j=n−1 на конец строки.

Если si​=sj​, то на концах строки находятся разные символы. А именно или 0 слева и 1 справа, или 1 слева и 0 справа. Это значит, что предыдущая строка могла быть увеличена в результате применения одной из заданных операций. В этом случае сдвигаем оба указателя i и j на одну позицию навстречу друг другу и снова проверяем, могла ли подстрока s[i...j] быть получена в результате операций Хусейна.

Как только для текущей подстроки s[i...j] будет выполняться si​=sj​, ее уже нельзя получить при помощи данных операций. Выводим ее длину – это и будет исходная строка Хусейна.

Пример

Проведем операции Хусейна в обратном порядке.

Строка s="1" изначально была у Хусейна.

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

Читаем входную строку. Вычисляем ее длину в переменной res.

cin >> s;
res = s.size();

Инициализируем два указателя: i=0 на начало и j=n−1 на конец строки.

i = 0; j = s.size() - 1;

Если si​=sj​, то на концах строки находятся разные символы. Хусейн мог при помощи своей операции получить строку s[i...j] из строки s[i+1...j−1].

while ((i < j) && (s[i] != s[j]))
{

Сдвигаем оба указателя i и j на одну позицию навстречу друг другу и уменьшаем на 2 текущий размер res подстроки s[i...j].

  i++; j--;
  res -= 2;
}

Выводим ответ.

cout << res << endl;
Eolymp #11244. Сумма двух

Задан массив A, отсортированный по возрастанию и содержащий n целых чисел. Определите, существует ли в нем такая пара чисел (Ai​,Aj​),i<j, сумма которых равна x.

Вход. Первая строка содержит два целых числа n (n≤105) и x (x≤106). Вторая строка содержит n целых неотрицательных чисел, каждое из которых не больше 106.

Выход. Выведите "YES" если такая пара элементов существует, и "NO" иначе.

Пример входа 1
10 13
1 3 5 6 8 10 11 11 11 16
Пример выхода 1
YES
Пример входа 2
8 61
5 5 8 12 16 21 44 50
Пример выхода 2
NO
Открыть задачу
Решение

Пусть v — входной массив чисел. Инициализируем указатели i=0 на начало массива, j=n−1 на конец массива. Массив по условию задачи уже отсортирован.

Пока указатели i и j не встретятся, совершаем следующие действия:

  • Если vi​+vj​=x, то искомая пара элементов найдена, выводим ее и завершаем программу.

  • Если vi​+vj​<x, то двигаем указатель i на одну позицию вправо. В этом случае сумма vi​+vj​ будет увеличиваться;

  • Если vi​+vj​>x, то двигаем указатель j на одну позицию влево. В этом случае сумма vi​+vj​ будет уменьшаться;

Пример

Рассмотрим первый тест. Инициализируем указатели. Промоделируем работу алгоритма. Значение x=13, ищем два числа с суммой 13.

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

Читаем входные данные.

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;

Указатель i двигаем вперед, указатель j двигаем назад.

while (i < j)
{

Если vi​+vj​=x, то искомая пара элементов найдена. Выводим ее и завершаем программу.

  if (v[i] + v[j] == x)
  {
    printf("YES\n");
    return 0;
  }

Если vi​+vj​<x, то двигаем указатель i на одну позицию вправо;

Если vi​+vj​>x, то двигаем указатель j на одну позицию влево;

  if (v[i] + v[j] < x) i++; else j--;
}

Если искомая пара не найдена, то выводим "NO".

printf("NO\n");
Eolymp #11246. Сумма трех

Задан массив целых чисел A и целое число x. Найдите такую тройку чисел (Ai​,Aj​,Ak​) в массиве, сумма которых равна x. Все индексы i,j,k должны быть различны.

Вход. Первая строка содержит размер массива n (n≤3⋅104) и значение x (∣x∣≤109). Вторая строка содержит n целых чисел, каждое из которых по модулю не больше 108.

Выход. Если требуемая тройка чисел существует, то выведите ее в любом порядке. Если существует несколько троек, выведите любую. Если искомой тройки не существует, выведите −1.

Пример входа
8 19
20 3 5 1 15 7 17 12
Пример выхода
1 3 15
Открыть задачу
Решение

Пусть a0​,a1​,...,an−1​ — входной массив. Переберем все возможные индексы k=0,1,...,n−3. Далее для каждого значения k найдем такую пару индексов (i,j) что i>k и ai​+aj​=x−ak​ (i<j). Это можно сделать на отсортированном массиве с помощью техники двух указателей за линейное время.

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

Читаем входные данные.

cin >> n >> x;
v.resize(n);
for (i = 0; i < n; i++)
  cin >> v[i];

Сортируем входной массив.

sort(v.begin(), v.end());

Перебираем значения k=0,1,...,n−3.

for (k = 0; k < n - 2; k++)
{

Ищем такую пару индексов (i,j), что i>k и vi​+vj​=x−vk​ (i<j). Инициализируем индексы i и j.

  i = k + 1; j = n - 1;
  s = x - v[k];

Используем технику двух указателей для поиска искомой пары (i,j).

  while (i < j)
  {
    if (v[i] + v[j] == s)
    {

Если пара (i,j) найдена, то выводим искомую тройку чисел (vk​,vi​,vj​).

      printf("%d %d %d\n", v[k], v[i], v[j]);
      return 0;
    }

Если v[i]+v[j]<s, то двигаем указатель i на одну позицию вправо;

Если v[i]+v[j]>s, то двигаем указатель j на одну позицию влево;

    if (v[i] + v[j] < s) i++; else j--;
  }
}

Если тройка чисел не найдена, то выводим −1.

cout << -1 << endl;
Eolymp #11286. В два раза больше

Задан отсортированный массив A из n целых чисел. Для каждого индекса i найдите, сколько элементов в массиве лежит между Ai​ и 2⋅Ai​ включительно.

Вход. Первая строка содержит размер n (n≤105) массива A. Вторая строка содержит n целых чисел в диапазоне от 0 до 109 в отсортированном порядке.

Выход. Выведите n чисел. Для каждого индекса i (1≤i≤n) массива выведите количество элементов, лежащих между Ai​ и 2⋅Ai​ включительно.

Пример входа
10
1 2 3 4 5 6 7 8 9 10
Пример выхода
2 3 4 5 6 5 4 3 2 1
Открыть задачу
Решение

Интервал A[i...j] будем называть хорошим, если на нем находятся числа в промежутке [Ai​;2⋅Ai​] включительно (то есть Aj​≤2⋅Ai​).

Реализуем скользящее окно при помощи двух указателей i и j и будем его поддерживать так, чтобы текущий рассматриваемый интервал [i...j] был хорошим, а интервал [i...j+1] плохим.

Например, для следующей последовательности

  • интервалы [2;2],[2;3],[2;4] и [2;5] являются хорошими.

  • интервалы [2;6],[2;7] являются плохими.

Если Aj​≤2⋅Ai​, то расширяем текущий интервал до А[i...j+1]. Иначе сокращаем его до А[i+1...j]. Перед передвижением указателя i выводим количество элементов, лежащих между Ai​ и 2⋅Ai​ включительно. Оно равно j−i.

Оценка сложности алгоритма линейная, то есть O(n).

Пример

Рассмотрим движение указателей для приведенного примера.

Для заданного состояния Aj​≤2⋅Ai​, поэтому двигаем вперед указатель j.

Теперь Aj​>2⋅Ai​. Вперед следует двигать указатель i. Однако перед его перемещением выводим количество элементов, лежащих между Ai​ и 2⋅Ai​ включительно. Оно равно j−i=6−2=4.

Двигаем j вперед пока Aj​≤2⋅Ai​.

Теперь Aj​>2⋅Ai​. Выводим ответ для i=3 (он равен j−i=8−3=5) и увеличиваем i на 1.

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

Читаем входные данные.

scanf("%d", &n);
for (i = 0; i < n; i++)
  scanf("%d", &a[i]);

Инициализируем указатели i=j=0.

i = j = 0;

Двигаем указатели вперед пока для каждого значения i<n не будет найден ответ.

while (i < n) // [i..j]
{

Если Aj​≤2⋅Ai​ (но при этом j не вышло за границы массива, то есть j<n), то расширяем текущий интервал, увеличивая указатель j.

  if ((j < n) && (a[j] <= 2 * a[i])) j++;
  else
  {

При Aj​>2⋅Ai​ выводим ответ для индекса i и увеличиваем i на единицу.

    printf("%d ", j - i);
    i++;
  }
}
Eolymp #10682. Отели на хорватском побережье

Вдоль прекрасного Адриатического побережья расположено n отелей. Каждый отель имеет свою стоимость в евро.

Петр выиграл m евро в лотерею. Теперь он хочет купить последовательность следующих друг за другом отелей так, чтобы сумма стоимостей этих последовательных отелей была как можно больше, но не превышала m.

Вы должны рассчитать эту максимально возможную общую стоимость.

Вход. В первой строке заданы два целых числа n и m (1≤n≤3⋅105,1≤m<231). В следующей строке заданы n натуральных чисел меньших 106, представляющих стоимости отелей в том порядке, в котором они расположены вдоль побережья.

Выход. Выведите искомую максимальную стоимость (оно будет больше 0 во всех тестах).

Пример входа
5 12
2 1 3 4 5
Пример выхода
12
Открыть задачу
Решение

Стоимости отелей занесем в массив a. Интервал [i...j] будем называть хорошим, если сумма стоимостей отелей на нем не больше m.

Реализуем скользящее окно при помощи двух индексов i и j и будем его поддерживать так, чтобы текущий рассматриваемый интервал [i...j] был хорошим. Пусть s — сумма стоимостей отелей на интервале [i...j]. Если s+a[j+1]≤m, то расширяем текущий интервал до a[i...j+1]. Иначе сокращаем его до a[i+1...j]. Находим максимум среди стоимостей всех рассматриваемых хороших интервалов.

Пример

Рассмотрим движение указателей для приведенного примера.

  1. i=0,j движется вперед пока сумма чисел на интервале [i...j] не больше m=12. Остановимся на интервале [0...3], так как на нем сумма равна 10, а на интервале [0...4] сумма равна 15.

  2. i=1,j подвинуть вперед не можем так как сумма на интервале [1...4] равна 13.

  3. i=2,j двигаем вперед до интервала [2...4].

Максимальное значение среди всех хороших интервалов равно 12 и достигается на интервале [2...4].

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

Читаем входные данные.

scanf("%d %lld", &n, &m);

Инициализируем переменные:

  • в res вычисляется максимальная стоимость среди стоимостей всех обрабатываемых хороших интервалов;

  • в s поддерживается сумма чисел на текущем интервале [i...j]. Все числа текущего интервала [i...j] содержатся в очереди q;

s = res = 0;

Последовательно обрабатываем стоимости отелей.

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

Читаем и добавляем стоимость val текущего отеля в очередь.

  scanf("%d", &val);
  q.push(val);

Обновляем сумму s на интервале. Все элементы текущего интервала находятся в очереди q.

  s += val;

Если сумма чисел на текущем интервале больше s, то удаляем элементы с начала интервала.

  while (s > m)
  {
    s -= q.front();
    q.pop();
  }

Вычисляем максимальное значение res среди сумм элементов допустимых интервалов (стоимость отелей на которых не превышает m).

  if (s > res) res = s;
}

Выводим ответ.

printf("%lld\n", res);
Eolymp #11721. Сумма подмассивов 1

Дан массив из n положительных целых чисел. Найдите количество подмассивов, сумма которых равна x.

Вход. Первая строка содержит размер массива n (1≤n≤2⋅105) и заданную сумму x (1≤x≤109). Следующая строка содержит n целых чисел a1​,a2​,...,an​ (1≤ai​≤109) — элементы массива.

Выход. Выведите требуемое количество подмассивов.

Пример входа
5 7
2 4 1 2 7
Пример выхода
3
Открыть задачу
Решение

Реализуем скользящее окно с помощью двух указателей i и j. Для каждого фиксированного i=1,2,... будем по максимуму раздвигать интервал [i...j] так, чтобы сумма элементов на нем была не больше x. То есть для каждого i ищем такое наибольшее значение j, что сумма элементов на интервале [i...j] не превышает x, а сумма элементов на интервале [i...j+1] уже больше x.

Пусть s — сумма чисел на интервале [i...j]. Если s+a[j+1]≤m, то расширяем интервал до [i...j+1]. Иначе сокращаем его до [i+1...j]. Подсчитываем количество интервалов с суммой x.

Пример

Рассмотрим движение указателей для приведенного примера.

  1. i=0, j движется вперед, пока сумма чисел на интервале [i...j] не станет больше x=7. Остановимся на интервале [0...2], так как сумма чисел на нем равна 7, а на интервале [0...3] сумма чисел уже равна 9.

  2. i=1, j двигаем вперед до 3. Сумма чисел на интервале [1...3] равна 7, а на интервале [1...4] уже 14.

  3. i=2, рассматриваемый интервал [2...3]. Сумма чисел на нем равна 3, однако двигать индекс j вперед мы не можем, потому что сумма чисел на интервале [2...4] равна 10, что больше x=7.

  4. i=3, рассматриваемый интервал [3...3]. Сумма чисел на нем равна 2, однако двигать индекс j вперед мы не можем, потому что сумма чисел на интервале [3...4] равна 9, что больше x=7.

  5. i=4, рассматриваемый интервал [4...4]. Сумма чисел на нем равна 7.

Количество подмассивов с суммой x=7 равно 3. Они начинаются с индексов 0,1 и 4.

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

Объявим рабочий массив.

#define MAX 200001
long long a[MAX];

Читаем входные данные.

scanf("%d %lld", &n, &x);
for (i = 0; i < n; i++)
  scanf("%lld", &a[i]);

Изначально установим текущий интервал [i...j]=[0;0]. Сумма чисел на этом интервале равна s=0.

s = j = 0;

Для каждого значения i последовательно обрабатываем интервалы [i...j].

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

Для фиксированного левого конца i интервала [i...j] ищем наибольшее j, для которого сумма элементов на указанном интервале не превосходит x.

  while ((j < n) && (s + a[j] <= x)) s += a[j++];

Если сумма чисел s на текущем интервале [i...j] равна x, то искомое количество подмассивов cnt увеличиваем на 1.

  if (s == x) cnt++;

Пересчитываем сумму чисел s для интервала [i+1...j].

  s -= a[i];
}

Выводим ответ.

printf("%lld\n", cnt);
Eolymp #9631. Соревнования последовательностей

Завтра Зия примет участие в соревновании последовательностей. Число x≥0 называется вершиной некоторой последовательности, если последовательность 1,2,3,...,x−1,x,x−1,...,3,2,1 является подпоследовательностью данной последовательности. Силой каждой последовательности считается ее наибольшая вершина.

Завтра все студенты пойдут на соревнование и победителем станет обладатель сильнейшей последовательности. Зия имеет последовательность a1​,a2​,a3​,...,an​. Он хочет завладеть системой оценки соревнования и удалить из нее последовательности с большей силой, чем у него. Однако Зия не знает силу собственной последовательности, но очень хочет победить. Помогите ему определить силу собственной последовательности.

Вход. В первой строке задано количество n (1≤n≤105) чисел в последовательности Зии. В следующей строке записаны n целых чисел ai​ (1≤ai​≤105) — элементы последовательности.

Выход. Выведите одно число — силу данной последовательности.

Пример входа 1
2
2 10
Пример выхода 1
0
Пример входа 2
3
1 2 3
Пример выхода 2
1
Пример входа 3
5
1 10 2 3 1
Пример выхода 3
2
Открыть задачу
Решение

Пусть v — входной массив чисел. Инициализируем указатели i=0 на начало массива и j=n−1 на конец массива. В переменной k будем подсчитывать силу последовательности. Изначально установим k=1.

Пока указатели i и j не встретятся, выполняем следующие действия:

  1. Сдвигаем указатель i на одну позицию вправо, если он не указывает на число k;

  2. Сдвигаем указатель j на одну позицию влево, если он не указывает на число k;

  3. Если оба указателя указывают на число k, увеличиваем k на единицу и сдвигаем каждый из указателей на одну позицию в соответствующем направлении;

После завершения работы алгоритма ответом будет значение k−1.

Пример

Рассмотрим второй тест. Инициализируем указатели. Двигаем указатель i вправо, а j влево пока они не будут указывать на число 1.

Двигаем указатель i вправо, а j влево, пока они не будут указывать на число 2.

Указатели встретились, останавливаем алгоритм. Ответом будет значение k=2.

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

Читаем входные данные.

scanf("%d", &n);
v.resize(n);
for (i = 0; i < n; i++)
  scanf("%d", &v[i]);

Установим указатели на начало и конец массива v.

int i = 0, j = v.size() - 1;

В переменной k подсчитываем силу последовательности.

k = 1;
while (i <= j)
{

Указатель i двигаем вправо пока не встретится число k.

  if (v[i] != k) i++;

Указатель j двигаем влево пока не встретится число k.

  if (v[j] != k) j--;

Если оба указателя указывают на число k, то увеличиваем k на единицу.

  if (v[i] == k && v[j] == k) k++;
}

Выводим ответ.

printf("%d\n", k - 1);
Eolymp #9632. Лучшая команда

Сегодня собрались n программистов. У каждого программиста есть рейтинг, показывающий его силу. Рейтинг — это целое число от 0 до 109. Ваш рейтинг как программиста равен m. Со всех собравшихся сегодня программистов Вы хотите выбрать двух в свою команду. Их необходимо выбрать таким образом, чтобы сумма их рейтингов была максимальной, но не превышала Ваш рейтинг, поскольку Вы хотите быть лидером этой команды.

Вход. В первой строке заданы два целых числа: n (2≤n≤105) — количество программистов и m (0≤m≤109) — Ваш рейтинг. Во второй строке приведены n целых чисел r1​,r2​,...,rn​ (0≤ri​≤109) — рейтинги программистов.

Выход. Выведите одно целое число — максимальную сумму рейтингов выбранных программистов или −1, если таких двух человек найти невозможно.

Пример входа 1
5 8
5 3 4 6 5
Пример выхода 1
8
Пример входа 2
7 19
8 4 25 1 20 5 12
Пример выхода 2
17
Пример входа 3
4 76
38 41 39 40
Пример выхода 3
-1
Открыть задачу
Решение

Отсортируем рейтинги программистов в массиве v. Поиск двух программистов с максимальным суммарным рейтингом будем осуществлять при помощи техники двух указателей.

Инициализируем указатель low=0 на начало массива и указатель high=n−1 на конец массива.

Среди всех возможных сумм v[low]+v[high], не превышающих m, находим максимальную. Если v[low]+v[high]≤m, сдвигаем указатель low вперед. В противном случае сдвигаем указатель high назад. Процесс передвижения указателей продолжаем до тех пор, пока они не встретятся.

Пример

Рассмотрим второй тест. Отсортируем числа и инициализируем указатели. Промоделируем работу алгоритма. В данном случае m=19: ищем два числа с максимальной суммой, не превышающей 19.

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

Читаем входные данные.

scanf("%d %d", &n, &m);
v.resize(n);
for (i = 0; i < n; i++)
  scanf("%d", &v[i]);

Сортируем рейтинги программистов.

sort(v.begin(), v.end());

Инициализируем указатели. В переменной mx вычисляем максимальную сумму двух элементов.

int mx = -1, low = 0, high = n - 1;

Указатель low двигаем вперед, указатель high двигаем назад.

while (low < high)
{

Среди всех возможных сумм v[low]+v[high], не больших m, находим максимум. Если v[low]+v[high]≤m, то двигаем вперед low. Иначе двигаем назад high.

  if (v[low] + v[high] <= m)
  {
    mx = max(mx, v[low] + v[high]);
    low++;
  }
  else high--;
}
C++
7 lines
108 bytes

Выводим ответ.

printf("%d\n", mx);
Eolymp #10568. Коллекционер алмазов (Бронза)

Корова Бесси, всегда увлекающаяся блестящими предметами, занялась добычей алмазов в свободное время! Она собрала n алмазов разных размеров и хочет разместить некоторые из них в витрине в амбаре.

Поскольку Бесси хочет, чтобы алмазы в витрине были относительно похожи по размеру, она решила, что не будет включать в витрину два алмаза, если их размеры различаются более чем на k (два алмаза могут быть выставлены вместе, если их размеры отличаются ровно на k). Зная значение k, помогите Бесси определить максимальное количество алмазов, которые она сможет выставить в витрине.

Вход. Первая строка содержит n (n≤1000) и k (0≤k≤10000). Каждая из следующих n строк содержит одно целое число — размер одного алмаза. Все размеры — положительные числа, не превышающие 10000.

Выход. Выведите максимальное количество алмазов, которое Беси сможет продемонстрировать в витрине амбара.

Пример входа
5 3
1
6
4
3
1
Пример выхода
4
Открыть задачу
Решение

Отсортируем массив с размерами алмазов. Для каждого индекса i рассмотрим последовательность mi​,mi+1​,...,mj​ такую, что ∣mj​−mi​∣≤k, но при этом ∣mj+1​−mi​∣>k. Среди всех таких последовательностей найдем ту, которая содержит наибольшее количество элементов.

Пример

Отсортируем размеры алмазов.

Рассмотрим интервал [0;3]. Разница между размерами алмазов на нем не превышает k=3. Однако разместить в амбаре все алмазы в интервале [0;4] Бесси уже не сможет, так как m4​−m0​>3.

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

Объявим массив для хранения размеров алмазов.

int m[1000];

Читаем входные данные.

scanf("%d %d", &n, &k);
for (i = 0; i < n; i++)
  scanf("%d", &m[i]);

Отсортируем массив.

sort(m, m + n);

Максимальное количество алмазов, которое Беси сможет разместить в амбаре, подсчитываем в переменной res.

res = 0;

Для каждого индекса i рассматриваем последовательность mi​,mi+1​,...,mj​ наибольшей длины, такую, что ∣mj​−mi​∣≤k. Длину этой последовательности подсчитываем в переменной cnt.

for (i = 0; i < n; i++)
{
  cnt = 0;
  for (j = i; j < n; j++)
  {

Как только условие ∣mj​−mi​∣>k становится истинным, выходим из цикла.

    if (m[j] > m[i] + k) break;
    cnt++;
  }

Среди длин всех последовательностей находим наибольшую.

  if (cnt > res) res = cnt;
}

Выводим ответ.

printf("%d\n", res);
Eolymp #11247. Контейнер с наибольшим количеством воды

Имеется массив h целых чисел длины n, задающий высоты n вертикальных линий. Для каждой i-й линии её конечные точки заданы координатами (i,0) и (i,hi​).

Найдите две такие линии, которые вместе с осью x образуют контейнер, способный удержать максимальное количество воды.

Вход. Первая строка содержит размер n (n≤105) массива h. Вторая строка содержит n натуральных чисел — элементы массива h, каждое из которых не превышает 109.

Выход. Выведите максимальный объём воды, который может удержать контейнер.

Пример входа
9
1 8 6 2 5 4 8 3 7
Пример выхода
49
Открыть задачу
Решение

Сначала вычислим объем воды в контейнере между крайними верткальными линиями, то есть между линиями пары (l,r)=(1,n). Затем будем двигать указатели l и r навстречу друг другу:

  • если hl​<hr​, то увеличим l на 1;

  • если hl​≥hr​, то уменьшим r на 1;

Объем воды между линиями l и r равен min(hl​,hr​)∗(r−l). Среди всех полученных значений находим наибольшее.

Задачу можно решить за O(n2). Однако из-за ограничения n≤105 такое решение приведёт к превышению лимита времени (Time Limit). Достаточно перебрать пары линий (i,j), для которых 1≤i<j≤n, и далее для каждой такой пары вычислить объем воды, который они могут удерживать. Среди всех вычисленных значений находим максимальный объём.

Пример

Контейнер из первого теста имеет вид:

Наибольшее количество воды можно удержать между линиями 2 и 9. Высота уровня воды составит 7, а объем воды равен 7⋅7=49.

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

Читаем входные данные.

scanf("%d", &n);
h.resize(n);
for (i = 0; i < n; i++)
  scanf("%lld", &h[i]);

В переменной res будем хранить максимальное количество воды, которое может удерживать контейнер.

long long res = 0;

Инициализируем пару указателей (l,r)=(1,n).

int l = 0, r = h.size() - 1;

Двигаем указатели l и r навстречу друг другу, пока они не встретятся.

while (l < r)
{

Объем воды между линиями l и r равен min(hl​,hr​)⋅(r−l). Среди всех таких объемов находим максимальный.

  res = max(res, min(h[l], h[r]) * (r - l));

Перемещаем указатели.

  if (h[l] < h[r])
    l += 1;
  else
    r -= 1;
}

Выводим ответ.

printf("%lld\n", res);
Eolymp #11340. Радио 106 FM

Вам дан список песен, которые играли на радио 106 FM. Всего в списке содержится n песен. Найдите длину самого длинного фрагмента списка, состоящего из неповторяющихся песен.

Вход. Первая строка содержит количество песен n (1≤n≤105) в списке. Вторая строка содержит n целых чисел k1​,k2​,...,kn​ (1≤ki​≤109‘) — идентификационные номера песен.

Выход. Выведите длину самого длинного фрагмента списка, состоящего из неповторяющихся песен.

Пример входа 1
8
1 2 1 3 2 7 4 2
Пример выхода 1
5
Пример входа 2
4
1 1 2 1
Пример выхода 2
2
Открыть задачу
Решение

Пусть массив v хранит идентификационные номера песен. С помощью двух указателей будем поддерживать скользящее окно [i;j], в котором песни не повторяются. Иными словами, на отрезке [i;j] все песни уникальны. Песни из этого отрезка будем хранить во множестве s. Для каждого значения i будем стремиться расширить отрезок до максимальной возможной длины. То есть для фиксированного i будем искать такое j, что на отрезке [i;j] песни не повторяются, а на отрезке [i;j+1] уже появляются дубли.

При обработке текущей песни с индексом j+1 имеются два варианта:

  • если песня vj+1​ не встречается в отрезке [i;j], то добавляем ее в этот отрезок, расширяя его до [i;j+1];

  • если песня vj+1​ уже присутствует в отрезке, сдвигаем левую границу i вправо до тех пор, пока vj+1​ не исчезнет из отрезка [i;j]. Затем добавляем vj+1​ в отрезок, расширяя его до [i;j+1]. При каждом сдвиге i удаляем соответствующие элементы из множества s;

Среди всех возможных отрезков [i;j] с уникальными песнями находим максимальную длину. Это и будет ответом на задачу.

Пример

Рассмотрим как будут изменяться отрезки с неповторяющимися песнями для первого примера.

Длина наибольшего отрезка равна 5.

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

Читаем входные данные. Идентификационные номера песен храним в массиве v.

scanf("%d", &n);
v.resize(n);
for (i = 0; i < n; i++)
  scanf("%d", &v[i]);

Поддерживаем отрезок [i;j−1] из неповторяющихся песен (то есть песни от vi​ до vj−1​ не повторяются). Во множестве s храним идентификационные номера песен из этого отрезка. В переменной res отслеживаем длину самого длинного фрагмента с уникальными песнями.

i = res = 0;
for (j = 0; j < n; j++)
{

Рассматриваем текущую песню с индексом j. Если она уже содержится во множестве s, то расширить текущий отрезок [i;j−1] за счет j-ой песни не удастся. В этом случае необходимо сдвигать левую границу i, удаляя из множества s песню vi​, до тех пор, пока j-ая песня не исчезнет из множества s.

  while (s.find(v[j]) != s.end())
  {
    s.erase(v[i]);
    i++;
  }

Добавим j-ую песню в отрезок и, соответственно, во множество s. Таким образом, отрезок [i;j] не будет содержать повторяющихся песен.

  s.insert(v[j]);

Среди всех возможных отрезков [i;j] находим самый длинный. Длина отрезка [i;j] равна j−i+1.

  res = max(res, j - i + 1);
}

Выводим ответ.

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

Список использованных задач

  • 2098. Переворачиватель

  • 11387. Игра в карточки

  • 11388. Игра в карточки 2

  • 11389. Игра Хусейна

  • 11244. Сумма двух

  • 11246. Сумма трех

  • 11286. В два раза больше

  • 10682. Отели на хорватском побережье

  • 11721. Сумма подмассивов 1

  • 9631. Соревнования последовательностей

  • 9632. Лучшая команда

  • 10568. Коллекционер алмазов (бронза)

  • 11247. Контейнер с наибольшим количеством воды

  • 11340. Радио 106 FM


18

Комментарии (5)

Загрузка

Момент, получаем данные с сервера
azimzadenihad•11 месяцев назад

thanks for all

0
Eltaj13•11 месяцев назад

Thanks for the problems!

1
drizzy•12 месяцев назад

Thanks for the problems! Will there be future topic contests like these ?

2
medv•12 месяцев назад•2 ревизия

drizzy Thanks a lot if you liked it. By the way, problems "Competition of sequences", "The best team" and "Radio 106 FM" are taken from the past official Azerbaijan School Competitions

7
drizzy•12 месяцев назад

medv Please continue this series of topic wise contests. Thanks

4