Basecamp
    Главная
    Задачи
    Соревнования
    Курсы
    Рейтинг
    Посты
    Store
    Discord
Educational Round #3 — Разборы
Войти
Разборы•21 час назад

Educational Round #3 — Разборы

Eolymp #7832. Количество максимальных

Дан массив из n целых чисел. Найдите количество максимальных элементов в массиве.

Вход. Первая строка содержит количество n (n≤100) элементов массива. Вторая строка содержит n целых чисел, каждое из которых по модулю не превышает 100.

Выход. Выведите количество максимальных элементов массива.

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

Найдем максимальный элемент массива. Затем выполняем повторный проход по массиву, чтобы подсчитать количество максимальных элементов.

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

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

int m[101];

Читаем входной массив. Находим его максимальный элемент max.

scanf("%d", &n);
max = -2000000000;
for (i = 0; i < n; i++)
{
  scanf("%d", &m[i]);
  if (m[i] > max) max = m[i];
}
C++
7 lines
123 bytes

В переменной cnt подсчитываем количество максимальных эементов.

cnt = 0;
for (i = 0; i < n; i++)
  if (m[i] == max) cnt++;

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

printf("%d\n", cnt);
Eolymp #8352. Такси

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

Вход. Три натуральных числа, не превосходящих 100 — количество пассажиров в первой, второй и третьей маршрутках соответственно.

Выход. Выведите одно число — наименьшее количество пассажиров, которое требуется пересадить. Если это выполнить невозможно, то выведите IMPOSSIBLE.

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

Пусть a,b,c — количество людей в первой, второй и третьей маршрутках. Для того чтобы после пересадки количество людей в маршрутках было одинаковое, необходимо чтобы сумма a+b+c делилась на 3.

Пусть d=(a+b+c)/3 — количество людей, которое должно находиться в каждой маршрутке после пересадки. Тогда из каждой маршрутки следует пересадить в какую-то другую столько людей, чтобы в ней осталось ровно d. Это возможно только если в маршрутке изначально находится больше d пассажиров. Например, из первой маршрутки следует пересадить a−d людей, если только a>d. Со второй маршрутки следует пересадить b−d людей (если b>d). Из третьей маршрутки следует пересадить c−d людей (если c>d).

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

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

scanf("%d %d %d",&a,&b,&c);

Если общая сумма людей не делится на 3, то выводим IMPOSSIBLE.

if((a + b + c) % 3 != 0)
  puts("IMPOSSIBLE");
else
{
  res = 0;

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

  d = (a + b + c) / 3;

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

  if (a > d) res += a - d;
  if (b > d) res += b - d;
  if (c > d) res += c - d;

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

  printf("%d\n",res);
}
Eolymp #7335. Кастрюли и крышки

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

Теперь школьный учитель хочет подсчитать потери и выяснить, как много новых кастрюль и крышек следует купить. Но сначала следует выяснить, сколько оставшихся кастрюль можно накрыть оставшимися крышками.

Кастрюли и крышки круглые. Крышка может покрыть кастрюлю, если только радиус крышки не меньше радиуса кастрюли.

Вход. Первая строка содержит числа n,m (1≤n,m≤1000) — количество оставшихся кастрюль и крышек. Вторая строка содержит n целых чисел ai​ (1≤ai​≤1000) — радиусы оставшихся кастрюль. Третья строка содержит m целых чисел bi​ (1≤bi​≤1000) — радиусы оставшихся крышек.

Выход. Выведите одно число — наибольшее количество кастрюль, которое может быть покрыто имеющимися крышками.

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

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

Пример

Рассмотрим какому наибольшему числу кастрюль можно подобрать крышки для заданного примера.

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

Объявим массивы, в которых будем хранить радиусы кастрюль и крышек.

#define MAX 1010
int pan[MAX], lid[MAX];

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

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

Сортируем радиусы кастрюль и крышек.

sort(pan,pan+n);
sort(lid,lid+m);

Используя жадный метод, ищем каждый раз наименьшую крышку, которой можно накрыть наименьшую кастрюлю.

for (i = j = 0; (i < n) && (j < m); j++)
  if (pan[i] <= lid[j]) i++;

Выводим количество накрытых кастрюль.

printf("%d\n",i);
Eolymp #1661. Рюкзак Алладина

Попав в пещеру с сокровищами, Алладин не стал брать старую почерневшую лампу. Вместо этого он принялся заполнять свой рюкзак золотыми монетами и драгоценными камнями. Конечно, ему хотелось забрать всё, но чудес не бывает — рюкзак имеет ограниченную грузоподъёмность и может не выдержать слишком большой вес.

Много раз он выкладывал одни предметы и заменял их другими, стремясь максимально увеличить общую стоимость содержимого рюкзака.

Требуется определить максимальную стоимость груза, который Алладин сможет унести.

Будем считать, что в пещере есть предметы n различных типов, причём количество предметов каждого типа не ограничено. Рюкзак может выдержать вес не более s. Каждый предмет типа i имеет вес wi​ и стоимость vi​ (i=1,2,...,n).

Вход. В первой строке заданы два натуральных числа s и n (1≤s≤250,1≤n≤35) — максимальный вес предметов в рюкзаке и количество типов предметов.

Далее следуют n строк, каждая из которых содержит два числа wi​ и vi​ (1≤wi​≤250,1≤vi​≤250) — вес и стоимость предмета типа i.

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

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

Пусть dpk​[s] — максимальная стоимость груза весом не более s, если разрешено использовать только предметы первых k типов.

Рассмотрим два возможных варианта при составлении груза весом i:

  • Не использовать предмет k-го типа: в этом случае оптимальное значение не изменится, то есть

dpk​[i]=dpk−1​[i]
  • Использовать предмет k-го типа: тогда его вес wk​ займёт часть вместимости рюкзака, а стоимость увеличится на vk​, то есть

dpk​[i]=dpk​[i−wk​]+vk​.

Так как требуется максимизировать стоимость груза, получаем рекуррентное соотношение:

dpk​[i]=max(dpk−1​[i],dpk​[i−wk​]+vk​),wk​≤i≤s

Базовые случаи:

dp0​[i]=0 и dpk​[0]=0

Пусть функция f(k,s) вычисляет максимальную стоимость груза, который можно собрать в рюкзак весом не более s, используя первые k типов предметов. Тогда имеет место рекуррентное соотношение:

f(k,s)=max(f(k−1,s),f(k,s−w[k])+v[k])

Осталось вычислить значение функции f(k,s) используя мемоизацию.

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

Объявим рабочие массивы:

  • w[i] — вес предмета i-го типа;

  • p[i] — стоимость предмета i-го типа;

  • dp[i] — максимальная стоимость груза весом не более i;

#define MAXW 252
#define MAXN 37
int w[MAXN], p[MAXN];
int dp[MAXW];

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

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

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

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

Пересчитываем значения массива dp, учитывая возможность использования предметов типа k. Поскольку количество предметов каждого типа не ограничено, каждый предмет можно выбирать несколько раз.

  for (i = w[k]; i <= s; i++)
    dp[i] = max(dp[i], dp[i - w[k]] + p[k]);
}

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

printf("%d\n", dp[s]);

Реализация алгоритма — рекурсия

Объявим рабочие массивы:

  • w[i] — вес предмета i-го типа;

  • p[i] — стоимость предмета i-го типа;

  • dp[i] — максимальная стоимость груза весом не более i;

#define MAXW 252
#define MAXN 37
#define INF 2000000000
int w[MAXN], v[MAXN];
int dp[MAXN][MAXW];

Функция f(k,s) вычисляет максимальную стоимость груза, который можно собрать в рюкзак весом не более s, используя первые k типов предметов.

int f(int k, int s)
{

Если k=0 или s=0, то рюкзак пуст, и его стоимость равна 0.

  if (k == 0 || s == 0) return 0;

Если s<0, то мы превысили допустимый вес, и этот вариант не имеет смысла. Поэтому возвращаем −INF (INF — это некоторое большое число).

  if (s < 0) return -INF;

Если значение f(k,s) уже вычислено, то возвращаем его.

  if (dp[k][s] != -1) return dp[k][s];

Вычисляем значение f(k,s) и запоминаем его в dp[k][s].

  return dp[k][s] = max(f(k - 1, s), f(k, s - w[k]) + v[k]);
}

Основная часть программы. Читаем входные данные.

scanf("%d %d", &s, &n);
for (i = 1; i <= n; i++)
  scanf("%d %d", &w[i], &v[i]);

Вычисляем и выводим ответ.

memset(dp, -1, sizeof(dp));
printf("%d\n", f(n, s));
Eolymp #11269. Лемурьи вечеринки - базовая

В подчинении у короля лемуров Джулиана находится ровно 2⋅k лемуров — по два лемура каждого из k видов. Джулиан обожает вечеринки, поэтому каждый вечер устраивает тусовку. Однако в VIP-зоне, к сожалению, хватает мест только для него самого и ещё n лемуров.

Поскольку Джулиан не любит повторяться, он старается каждый раз приглашать в VIP-зону такой набор лемуров, который ранее ещё не встречался. Два лемура одного вида считаются неразличимыми. Два набора лемуров считаются одинаковыми, если они совпадают как мультимножества видов лемуров.

Помогите Джулиану определить, сколько различных вечеринок он сможет провести. Так как ответ может быть очень большим, выведите его по модулю 109+7.

Вход. В одной строке заданы два целых числа k и n (1≤k≤500000,0≤n≤2⋅k) — количество видов лемуров и число мест в VIP-зоне (не считая самого Джулиана).

Выход. Выведите одно число — количество различных вечеринок по модулю 109+7.

Sample input 1
3 3
Sample output 1
7
Sample input 2
4 3
Sample output 2
16
Открыть задачу
Решение

На n мест некоторые виды будут представлены двумя лемурами, а некоторые одним. Пусть выбрано i пар лемуров (то есть i видов представлены двумя особями). Сделать такой выбор можно Cki​ способами. После этого в VIP зоне останется n−2i свободных мест. Эти места можно занять лемурами из k−i оставшихся видов, выбирая по одному лемуру из каждого. Количество способов выбрать таких лемуров вычисляется по формуле Ck−in−2i​.

Так как значение i может принимать любое целое значение от 0 до k, получаем итоговый ответ как сумму по всем возможным i:

i=0∑k​Cki​⋅Ck−in−2i​

Пример

Рассмотрим второй пример, в котором есть k=4 пары лемуров и n=3 места в VIP-зоне. По формуле получаем:

i=0∑k​C4i​⋅C4−i3−2i​=C40​⋅C43​+C41​⋅C31​=4+12=16

Выпишем только ненулевые слагаемые. Значение Cnk​ будем считать равным нулю, если выполняется одно из трех неравенств: k<0, n<0 или k>n.

Обозначим лемуров буквами {a,a,b,b,c,c,d,d}, где одинаковые буквы соответствуют лемурам одного вида.

Слагаемому C40​⋅C43​=4 соответствует ситуация, когда ни одна пара лемуров одного вида не выбрана, то есть каждый из трёх выбранных лемуров принадлежит разным видам. Возможные четыре выборки следующие:

Рассмотрим слагаемое C41​⋅C31​=12. В этом случае из одного вида выбираются два лемура (существует 4 варианта такого выбора). На оставшееся одно место выбирается один лемур из трёх оставшихся видов. Таким образом, всего возможно 12 различных вариантов:

Очевидно, что разместить на 3 местах по два лемура из двух разных видов невозможно. Поэтому все остальные слагаемые суммы равны нулю.

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

Объявим константы.

#define MAX 1000001
#define MOD 1000000007

Объявим массивы:

  • fact содержит факториалы чисел по модулю MOD;

  • factinv содержит значения, обратные факториалам чисел по модулю MOD:

fact[n] = n! mod 1000000007
factinv[n] = n! -1 mod 1000000007

typedef long long ll;
ll fact[MAX], factinv[MAX];

Функция pow вычисляет степень числа по модулю: xn mod p.

ll pow(ll x, ll n, ll p)
{
  if (n == 0) return 1;
  if (n % 2 == 0) return pow((x * x) % p, n / 2, p);
  return (x * pow(x, n - 1, p)) % p;
}

Функция inverse находит число, обратное x по простому модулю p. Поскольку p — простое число, по малой теореме Ферма выполняется равенство:

xp−1 mod p = 1 для всякого 1≤x<p

Это равенство можно переписать в виде:

(x⋅xp−2) mod p = 1,

откуда следует, что обратным к x является число xp−2 mod p.

ll inverse(ll x, ll p)
{
  return pow(x, p - 2, p);
}

Функция Cnk вычисляет значение биномиального коэффициента по следующей формуле:

Cnk​=k!(n−k)!n!​
ll Cnk(int n, int k)
{
  return ((fact[n] * factinv[k]) % MOD * factinv[n - k]) % MOD;
}

Основная часть программы. Читаем входные данные.

scanf("%d %d", &k, &n);

Заполняем массивы факториалов fact и factinv.

fact[0] = 1;
for (i = 1; i < MAX; i++)
  fact[i] = (fact[i - 1] * i) % MOD;

factinv[0] = 1;
for (i = 1; i < MAX; i++)
   factinv[i] = inverse(fact[i], MOD);
C++
7 lines
165 bytes

Вычисляем ответ по формуле ∑i=0k​Cki​⋅Ck−in−2i​. Значение биномиального коэффициента Cnk​ считаем равным нулю, если выполняется одно из трех условий: k<0, n<0 или k>n.

res = 0;
for (i = 0; i <= k; i++)
{
  if (n - 2 * i < 0 || k - i < 0 || n - 2 * i > k - i) continue;
  res = (res + Cnk(k, i) * Cnk(k - i, n - 2 * i)) % MOD;
}

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

printf("%lld\n", res);
Eolymp #973. Максимальная сумма на дереве

Дано дерево с n вершинами, где вершина с номером i (1≤i≤n) содержит ci​ монет. Выберите такое подмножество вершин, чтобы никакие две из них не были смежными (то есть не соединены ребром), а сумма монет в выбранных вершинах была максимальной.

Вход. Первая строка содержит количество вершин n (1≤n≤105) в дереве. Каждая из следующих n−1 строк содержит два целых числа u и v (1≤u,v≤n), задающих ребро в дереве. Последняя строка содержит n неотрицательных целых чисел c1​,...cn​ — количество монет в каждой вершине дерева.

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

Sample input 1
5
1 2
1 3
2 4
2 5
1 5 7 1 2
Sample output 1
12
Sample input 2
5
1 2
1 3
2 4
2 5
3 7 5 10 1
Sample output 2
16
Открыть задачу
Решение

Пусть v — вершина дерева. Обозначим через:

  • dp1​(v) наибольшую сумму монет, которую можно собрать из поддерева с корнем v, если монеты в вершине v мы берем.

  • dp2​(v) наибольшую сумму монет, которую можно собрать из поддерева с корнем v, если монеты в вершине v мы не берем.

Тогда ответом на задачу будет max(dp1​(1),dp2​(1)), если взять первую вершину за корень дерева.

Определим рекурсивно приведенные функции:

  • Если монеты в вершине v мы берем, то брать монеты из ее сыновей нельзя:

dp1​(v)=cv​+i=1∑k​dp2​(toi​),где to1​,...,tok​−сыновья вершины v.
  • Если монеты в вершине v мы не берем, то из её сыновей можно или брать, или не брать монеты. Из этих двух вариантов следует выбрать тот, в котором сумма монет максимальна:

dp2​(v)=i=1∑k​max(dp1​(toi​),dp2​(toi​)),где to1​,...,tok​−сыновья вершины v.

Если v — лист дерева с cv​ монетами, то функции в нем примут следующие значения: dp1​(v)=cv​ и dp2​(v)=0.

Пример

Расставим метки dp1​(v)/dp2​(v) на вершинах деревьев из примеров.

Для первого примера имеем:

  • dp1​(1)=c1​+dp2​(2)+dp2​(3)=1+3+0=4;

  • dp2​(1)=max(dp1​(2),dp2​(2))+max(dp1​(3),dp2​(3))=5+7=12;

  • dp1​(2)=c2​+dp2​(4)+dp2​(5)=5+0+0=5;

  • dp2​(2)=max(dp1​(4),dp2​(4))+max(dp1​(5),dp2​(5))=1+2=3;

Для второго примера имеем:

  • dp1(1)=c1​+dp2​(2)+dp2​(3)=3+11+0=14;

  • dp2​(1)=max(dp1​(2),dp2​(2))+max(dp1​(3),dp2​(3))=11+5=16;

  • dp1​(2)=c2​+dp2​(4)+dp2​(5)=7+0+0=7;

  • dp2​(2)=max(dp1​(4),dp2​(4))+max(dp1​(5),dp2​(5))=10+1=11;

Упражнение

Расставьте метки dp1​(v)/dp2​(v) на вершинах дерева:

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

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

vector<vector<int> > g;
vector<int> dp1, dp2, cost;

Функция dfs реализует поиск в глубину. Вычисляем значения dp1​ и dp2​ во всех вершинах дерева.

void dfs(int v, int p = -1)
{
  dp1[v] = cost[v];
  dp2[v] = 0;

  for (int to : g[v])
  {
    if (to == p) continue;

    dfs(to, v);

    dp1[v] += dp2[to];
    dp2[v] += max(dp1[to], dp2[to]);
  }
}
C++
15 lines
217 bytes

Основная часть программы. Читаем дерево и массив монет.

scanf("%d",&n);
g.resize(n+1);
for(i = 1; i < n; i++)
{
  scanf("%d %d",&u,&v);
  g[u].push_back(v);
  g[v].push_back(u);
}

dp1.resize(n+1); dp2.resize(n+1);
cost.resize(n+1);
for(i = 1; i <= n; i++)
  scanf("%d",&cost[i]);
C++
13 lines
238 bytes

Пусть корень дерева находится в вершине 1. Запускаем из нее поиск в глубину.

dfs(1);

Вычисляем и выводим ответ.

ans = max(dp1[1], dp2[1]);
printf("%d\n",ans);
Eolymp #11722. Разрезание прямоугольника

Дан прямоугольник размером a×b. Ваша задача — разрезать его на квадраты. За один ход можно выбрать один из прямоугольников и разрезать его на два новых прямоугольника так, чтобы все длины сторон оставались целыми числами. Определите наименьшее количество ходов, которое потребуется для этого.

Вход. Два целых числа a и b (1≤a,b≤500).

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

Sample input 1
3 5
Sample output 1
3
Sample input 2
5 10
Sample output 2
1
Открыть задачу
Решение

Пусть f(a,b) — наименьшее количество ходов, за которое прямоугольник размером a×b можно разрезать на квадраты. Будем хранить это значение в ячейке dp[a][b].

Если прямоугольник уже является квадратом (a=b), разрезы не требуются, поэтому f(a,a)=0.

Прямоугольник a×b можно разрезать либо вертикальным, либо горизонтальным разрезом. Рассмотрим сначала вертикальный разрез.

При вертикальном разрезе прямоугольник a×b делится на два прямоугольника: a×k и a×(b−k). Чтобы полученные прямоугольники были невырожденными, должно выполняться условие 1≤k<b. Затем рекурсивно решаем задачу для каждого из этих двух прямоугольников и добавляем 1 к результату, так как мы сделали один вертикальный разрез. При этом выбираем такое k, для которого сумма f(a,k)+f(a,b−k)+1 наименьшая:

f(a,b)=1≤k<b​min​(f(a,k)+f(a,b−k))+1

Для случая горизонтального разреза получаем аналогичную формулу:

f(a,b)=1≤k<a​min​(f(k,b)+f(a−k,b)+1

Остаётся выбрать минимальное значение среди этих двух вариантов.

Пример

В первом тесте требуется выполнить 3 разреза:

  • Первым разрезом прямоугольник 3×5 разделяется на 3×3 и 2×3;

  • Вторым разрезом прямоугольник 2×3 разделяется на 2×2 и 2×1;

  • Третьим разрезом прямоугольник 2×1 разделяется на 1×1 и 1×1;

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

Объявим константы и рабочий массив.

#define MAX 501
#define INF 0x3F3F3F3F

int dp[MAX][MAX];

Функция f(a,b) возвращает наименьшее количество ходов, за которое прямоугольник размером a×b можно разрезать на квадраты.

int f(int a, int b)
{

Если прямоугольник уже является квадратом (a=b), разрезы не требуются.

  if (a == b) return 0;

Если значение f(a,b) еще не вычислено (dp[a][b]=∞), то вычисляем его.

  if (dp[a][b] == INF)
  {

Выполняем все возможные вертикальные разрезы.

    for (int k = 1; k < b; k++)
      dp[a][b] = min(dp[a][b], f(a, k) + f(a, b - k) + 1);

Выполняем все возможные горизонтальные разрезы.

    for (int k = 1; k < a; k++)
      dp[a][b] = min(dp[a][b], f(k, b) + f(a - k, b) + 1);
  }

Возвращаем результат f(a,b)=dp[a][b].

  return dp[a][b];
}

Основная часть программы. Читаем размеры прямоугольника a и b.

scanf("%d %d", &a, &b);

Вычисляем и выводим ответ.

memset(dp, 0x3F, sizeof(dp));
printf("%d\n", f(a, b));
Eolymp #1405. Палочки

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

Отец Волк дождался, пока его волчата немного подросли и начали понемногу бегать, и в одну из тех ночей, когда собиралась Стая, повёл их — вместе с Маугли и Матерью Волчицей — на Скалу Совета. Это была вершина холма, усеянная крупными валунами, за которыми могла укрыться целая сотня волков. Акела, большой серый волк-одиночка, избранный вожаком всей Стаи за силу и ловкость, взывал со своей скалы:

— Закон вам известен, закон вам известен! Смотрите же, волки!

Отец Волк вытолкнул на середину круга Лягушонка Маугли. Тот сел на землю, засмеялся и стал играть палочками.

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

Вход. Первая строка содержит количество палочек n (1≤n≤16). Во второй строке заданы их длины — натуральные числа в диапазоне от 1 до 108.

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

Sample input
8
7 1 5 2 3 2 4 5 
Sample output
49
Открыть задачу
Решение

Пусть S — данное множество палочек. Необходимо найти два непересекающихся подмножества A и B из S, такие, что палочки каждого из них можно разбить на два подмножества с равной суммой длин.

Для каждого подмножества mask⊆S вычислим сумму длин его палочек и запишем в sum[mask].

Переберём все подмножества I⊂S. Для каждого подмножества I переберём все его подмножества J⊂I. Если существует такое J, что 2⋅sum[J]=sum[I] (то есть суммарная длина палочек в J вдвое меньше суммы длин всех палочек из I), то подмножество I можно использовать в качестве противоположных сторон прямоугольника. В этом случае установим can[I]=true.

Вся описанная процедура выполняется за O(3n).

Переберём все подмножества множества палочек. Если для некоторого множества I существует подмножество J⊂I такое, что can[J]=true и can[I xor J]=true (то есть множества J и I xor J не пересекаются), то тем самым получаем одно из возможных распределений палочек между горизонтальными и вертикальными сторонами прямоугольника.

Длины сторон такого прямоугольника равны sum[J]/2 и sum[I xor J]/2. Среди всех найденных вариантов выбираем прямоугольник с максимальной площадью.

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

Длины палочек хранятся в массиве a. Для каждого подмножества палочек mask сохраняем их суммарную длину в массиве sum[mask]. Значение can[mask] устанавливаем в true, если множество mask можно разделить на два подмножества с равной суммой длин — им будут соответствовать противоположные стороны прямоугольника.

#define MAX 16
int a[MAX];
int sum[1 << MAX];
bool can[1 << MAX];

Основная часть программы. Читаем входные данные.

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

Переберём все подмножества палочек и вычислим суммарную длину каждого из них.

for (i = 0; i < 1 << n; i++)
  for (j = 0; j < n; j++)
    if (i & (1 << j)) sum[i] += a[j];

Затем переберём все возможные подмножества множества палочек. Если для некоторого множества I существует подмножество J⊂I такое, что суммарная длина палочек в J вдвое меньше суммарной длины палочек в I, то установим can[I]=true. В этом случае палочки из множества I можно использовать как противоположные стороны прямоугольника.

for (i = 1; i < 1 << n; i++) 
{
  int s = sum[i];
  for (j = i; j > 0; j = (j - 1) & i)
    if (sum[j] * 2 == s) 
    {
      can[i] = true;
      break;
    }
}
C++
10 lines
172 bytes

Переберём все подмножества множества палочек. Если для некоторого множества I существует подмножество J⊂I такое, что can[J]=true и can[I xor J]=true, то получаем одно из возможных распределений палочек между горизонтальными и вертикальными сторонами прямоугольника. Длины сторон такого прямоугольника равны sum[J]/2 и sum[IxorJ]/2.

res = 0;
for (i = 1; i < 1 << n; i++)
  for (j = i; j > 0; j = (j - 1) & i)
    if (can[j] && can[i ^ j])
      res = max(res, sum[j] / 2 * 1LL * sum[i ^ j] / 2);

Выводим площадь найденного прямоугольника максимального размера.

printf("%lld\n",res);
Eolymp #4073. Это убийство!

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

Вам необходимо найти сумму всех чисел, записанных детективом в его дневнике.

Вход. Первая строка содержит число t (t≤10) — количество тестов. Далее следуют 2t строк. В первой из них указано число n (1≤n≤105) — количество ступенек. В следующей строке записаны n чисел — значения, нанесенные на ступеньки. Все числа находятся в диапазоне от 0 до 106.

Выход. Для каждого теста выведите в отдельной строке итоговую сумму.

Sample input
1
5
1 5 3 6 4
Sample output
15
Открыть задачу
Решение

Числа, записанные на ступеньках, являются целыми и находятся в диапазоне от 0 до 106 включительно. Построим дерево Фенвика на массиве a длины 106+1 (индексы массива изменяются от 0 до 106 включительно), изначально заполненном нулями.

Каждый раз, когда детектив проходит ступеньку со значением value, увеличим avalue​ на value. При этом в дневнике он запишет сумму:

a0​+a1​+a2​+...+avalue−1​

С учетом ограничений на входные данные, все вычисления следует выполнять в 64-битовом целочисленном типе.

Пример

Смоделируем записи детектива на приведённом примере. Индексация массива начинается с 0. Числа, записанные детективом, указаны под стрелками.

Сумма чисел, записанных в дневнике детектива, равна

0+1+1+9+4=15
Реализация алгоритма

Пусть максимальный размер массива равен MAX. Создадим дерево Фенвика Fenwick.

#define MAX 1000001
vector<long long> Fenwick;

Функция Summa0_i вычисляет значение суммы a0​+a1​+...+ai​.

long long Summma0_i(int i)
{
  long long result = 0;
  for (; i >= 0; i = (i & (i + 1)) - 1)
    result += Fenwick[i];
  return result;
}
C++
7 lines
145 bytes

Функция IncElement увеличивает значение элемента: ai​=ai​+delta.

void IncElement(int i, int delta)
{
  for (; i < MAX; i = (i | (i+1)))
    Fenwick[i] += delta;
}

Основная часть программы. Читаем входные данные.

scanf("%d",&tests);
while (tests--)
{
  scanf("%d",&n);

Заполняем массив Fenwick нулями.

  Fenwick.assign(MAX,0);

Искомую сумму, записанную в дневнике детектива, будем подсчитывать в переменной sum.

  sum = 0;

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

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

Для каждого значения value, записанного на ступеньке, увеличиваем avalue​ на value, а также вычисляем сумму всех ранее встреченных чисел, меньших value. Эта сумма равна a0​+a1​+a2​+...+avalue−1​.

    scanf("%d",&value);
    IncElement(value, value);
    sum += Summma0_i(value - 1);
  }

Выводим сумму всех чисел, записанных в дневнике детектива.

  printf("%lld\n",sum);
}
Eolymp #10456. Сервер

Амин и Мурад решили создать игровой сервер "Майнкрафт".

На торжественное открытие сервера они пригласили k гостей. Гости живут в разных городах, и при подключении к серверу (в зависимости от их местоположения) будут испытывать определённую задержку (ping). Осознав возможные трудности, Амин и Мурад решили выбрать самое оптимальное место для размещения сервера.

Имеется n городов, пронумерованных от 1 до n, и m двусторонних каналов связи между ними. Подключение к серверу возможно только по этим каналам. С их помощью можно установить соединение между любыми двумя городами. При этом между двумя городами может быть не более одного прямого канала связи, а ни один город не соединён каналом сам с собой. Для каждого канала указана задержка передачи wi​.

Задержкой соединения между каким-либо городом и сервером считается минимально возможная сумма задержек по каналам на пути из этого города до сервера.

Амин и Мурад хотят выбрать такой город для размещения сервера, чтобы суммарная задержка соединений всех гостей была минимальной. Если сервер размещён в том же городе, где живёт гость, то для него задержка считается равной нулю.

Если существует несколько городов с одинаковой минимальной суммарной задержкой, то Амин и Мурад выберут город с наименьшим номером.

Определите город, в котором будет размещён сервер, и какова будет суммарная задержка соединений всех гостей к серверу.

Вход. В первой строке заданы три целых числа n (1≤n≤104),m (1≤m≤4∗104),k (1≤k≤100) — количество городов, количество каналов связи и количество гостей соответственно. Во второй строке записаны k различных чисел ci​ (1≤ci​≤n) — номера городов, в которых живут гости.

В каждой из следующих m строк даны три целых числа ui​,vi​,wi​ (1≤ui​,vi​≤n) — описание двустороннего канала связи между городами ui​ и vi​ с задержкой wi​ (1≤wi​≤104).

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

Sample input
5 6 3
1 2 5
1 2 10
1 4 3
2 4 2
2 5 8
3 4 5
3 5 3
8 lines
57 bytes
Sample output
2 13
Открыть задачу
Решение

Поскольку n≤104, для представления графа воспользуемся списком смежности.

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

Отметим, что сервер не обязательно должен находиться в городе, где проживает один из гостей.

Пример

Граф, приведенный в примере, имеет следующий вид:

Запустим алгоритм Дейкстры из вершин, в которых проживают гости: 1,2 и 5. Для каждой из этих вершин определим кратчайшие расстояния до всех остальных городов.

Затем для каждой вершины графа найдём сумму расстояний до всех городов с гостями. Минимальная сумма расстояний равна 13, и она достигается в вершине с наименьшим номером 2.

Таким образом, сервер следует разместить в городе 2. Суммарное расстояние от него до городов, где проживают гости, составляет 5+0+8=13.

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

Объявим константу бесконечность.

#define INF 0x3F3F3F3F

Структура edge будет хранить информацию о ребре: вершину node, в которую ведёт ребро, и вес dist этого ребра.

struct edge
{
  int node, dist;
  edge(int node, int dist) : node(node), dist(dist) {}
};

Объявим компаратор для сортировки пар (node,dist) по убыванию значения dist.

bool operator< (edge a, edge b)
{
  return a.dist > b.dist;
}

Объявим список смежности для представления графа.

vector<vector<edge> > g;

Функция Dijkstra реализует алгоритм Дейкстры. На вход она принимает граф g и начальную вершину start. Возвращаемым значением является массив d, где d[i] содержит кратчайшее расстояние от вершины start до вершины i.

void Dijkstra(vector<vector<edge> >& g, vector<int>& d, int start)
{
  priority_queue<edge> pq;
  pq.push(edge(start, 0));

  d = vector<int>(n + 1, INF);
  d[start] = 0;

  while (!pq.empty())
  {
    edge e = pq.top(); pq.pop();
    int v = e.node;

    if (e.dist > d[v]) continue;

    for (auto e : g[v])
    {
      int to = e.node;
      int cost = e.dist;
      if (d[v] + cost < d[to])
      {
        d[to] = d[v] + cost;
        pq.push(edge(to, d[to]));
      }
    }
  }
}
C++
27 lines
513 bytes

Основная часть программы. Читаем входные данные.

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

Читаем входной взвешенный граф.

g.resize(n + 1);
for (i = 0; i < m; i++)
{
  scanf("%d %d %d", &a, &b, &w);
  g[a].push_back(edge(b, w));
  g[b].push_back(edge(a, w));
}
C++
7 lines
145 bytes

Запускаем алгоритм Дейкстры из городов, в которых проживают гости.

dp.resize(n + 1);
for (i = 0; i < k; i++)
{
  Dijkstra(g, dist, c[i]);

  for (j = 1; j <= n; j++)
   dp[j] += dist[j];
}
C++
8 lines
130 bytes

На данный момент dp[i] содержит сумму кратчайших расстояний от вершины i до всех городов, в которых находятся гости. Остаётся найти минимальное значение среди элементов массива dp и вывести соответствующий ответ. Переменная ptr хранит наименьший номер вершины, в которой следует разместить сервер.

res = INF;
for (i = 1; i <= n; i++)
{
  if (dp[i] < res)
  {
    res = dp[i];
    ptr = i;
  }
}
C++
9 lines
106 bytes

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

printf("%d %d\n", ptr, dp[ptr]);

2

Комментарии

Загрузка

Момент, получаем данные с сервера

Пока что ничего

Будьте первым, кто начнет обсуждение!
Войти