1

Тема: Algotester, задача #40427 "Новий рік"

Використання бінарного пошуку для вирішування задачі

Зараз застрягла на цій задачці:
https://algotester.com/uk/ArchiveProble … itor/40427
Або:
Діти на засніженому подвір’ї зробили, себто накатуляли, багато снігових куль. І от настав час іти додому. Але ж дітям не хочеться залишати їхні неймовірні снігові шедеври надворі. От і вирішили вони позабирати їх до хати. Мама кожної дитини дозволяє їй занести до хати не більше ніж одну кулю, або її частинку. Ба більше, якщо якесь дитинча візьме додому більшу частину, то інше на нього образиться.

От діти і задумались над тим, яким чином занести до їхніх домівок максимальну кількість снігу. Причому всі несуть додому по одному куску кулі однакового об’єму. Діти можуть ділити кулі, які вони мають в наявності як завгодно, але ліпити два куски докупи вони не можуть (сніг дуже сухий попався).

Вам необхідно знайти максимально можливий сумарний об’єм снігу, який діти зможуть рознести по своїх домівках.

Вхідні дані
У першому рядку містяться два цілих числа n і m — кількість дітей та кількість снігових куль.

Наступні m рядків містять по одному цілому числу r з індексом i — радіусу відповідної кулі.

Вихідні дані
У єдиному рядку виведіть дійсне число — максимальний можливий об’єм снігу. Відповідь уважатиметься правильною, якщо абсолютна або відносна похибка не перевищуватиме 10^(-4).

Обмеження
1≤ n, m ≤10^(5),

1≤ r з індексом і ≤100.

Є проблема з тим випадком, коли снігових куль менше за кількість дітей. В наступному коді я використовую бінарний пошук для вирішення цієї задачі:

#define _USE_MATH_DEFINES

#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>

const long double pi = M_PI; // 3.1415926535897932384626433832795;
void QuickSort(int radius[], int first, int last);
double maximum(long double snow[], int children, int snowballs);

using namespace std;

int main() {
    int children = 1, snow_balls = 1;
    cin >> children >> snow_balls;
    int *snow_radius = new int[snow_balls];
    for(int i = 0; i < snow_balls; i++) {
        cin >> snow_radius[i];
    }
    QuickSort(snow_radius, 0, snow_balls - 1);
    long double *new_snow = new long double[snow_balls];
    for(int i = 0; i < snow_balls; i++) {
        new_snow[i] = (snow_radius[i] * snow_radius[i] * snow_radius[i] * pi * 4) / 3;
    }
    cout << fixed << setprecision(10) << maximum(new_snow, children, snow_balls) * children;
    delete[] new_snow;
    delete[] snow_radius;
    return 0;
}
void QuickSort(int snow_radius[], int first, int last) {
    int middle = (first + last) / 2;
    int left_bound = first;
    int right_bound = last;
    do {
        while(snow_radius[left_bound] > snow_radius[middle]) {
            left_bound++;
        }
        while(snow_radius[right_bound] < snow_radius[middle]) {
            right_bound--;
        }
        if(left_bound <= right_bound) {
            swap(snow_radius[left_bound++], snow_radius[right_bound--]);
        }
    } while(left_bound <= right_bound);
    if(left_bound < last) QuickSort(snow_radius, left_bound, last);
    if(right_bound > first) QuickSort(snow_radius, first, right_bound);
}
double maximum(long double snow[], int children, int snow_balls) {
    long double left_bound = 0, right_bound = ((pow(100, 3) * pi * 4) / 3) + 1;
    int max_value = min(children, snow_balls);
    long double mid_radius;
    int max_radius;
    double eps = 1e-7;
    while(right_bound - left_bound > eps) {
        mid_radius = (left_bound + right_bound) / 2;
        max_radius = 0;
        for(int j = 0; j < max_value; j++) {
            max_radius += snow[j] / mid_radius;
        }
        if(max_radius >= children)
            left_bound = mid_radius;
        else
            right_bound = mid_radius;
    }
    return left_bound;
}

Я перевіряла на всі можливі інпути, перевіряла через чат-gpt (не сильно допомогло), але кожного разу виникає "неправильна відповідь" у 16 тесті на сайті з задачею. Можливо у мене проблеми з округленням, хоч я максимально округляю об'єм кулі.

Подякували: leofun011

2

Re: Algotester, задача #40427 "Новий рік"

ramired написав:

Використання бінарного пошуку для вирішування задачі

Простіше використати стандартну функцію std::sort(..), щоб зосередитись на алгоритмі для цієї задачі. Сортуваня тут - тільки перший крок.

algotester.com написав:

.. не більше ніж одну кулю, або її частинку ..

Це найважливіша частина. Підкину пару прикладів для роздумів.

Приклади

Приклад даних #1

Ввід :
5 3
9
8
6

Вивід :
5089.380098815465

Поясненя :
9^3 = 729 = 243 * 3 +   0
8^3 = 512 = 243 * 2 +  26
6^3 = 216 = 243 * 0 + 216

243 * 5 * (4 / 3 * pi) = 5089.380098815465..

Приклад даних #2

Ввід :
6 3
9
8
6

Вивід :
5428.67210540316

Поясненя :
9^3 = 729 = 216 * 3 + 81
8^3 = 512 = 216 * 2 + 80
6^3 = 216 = 216 * 1 +  0

216 * 6 * (4 / 3 * pi) = 5428.67210540316..

3

Re: Algotester, задача #40427 "Новий рік"

Запустив програму ramired, приємно вражений, мої тести вона прекрасно проходить.
Треба більше прикладів придумати, щоб піймати програму на не правильному результаті.

4 Востаннє редагувалося leofun01 (02.04.2023 16:18:51)

Re: Algotester, задача #40427 "Новий рік"

Я затупив, там 2, не 3, (27 != 12.8 * 3 + 1.4)

Я знайшов приклад, на якому програма ламається.

Приклад даних #3

Ввід :
17 3
5
4
3

Вивід (очікуваний) :
911.4807485615

Поясненя :
5^3 = 125 = 12.8 * 9 + 9.8
4^3 =  64 = 12.8 * 5 + 0
3^3 =  27 = 12.8 * 3 + 1.4

12.8 * (9 + 5 + 3) * (4 / 3 * pi) = 911.4807485615..

Вивід програми (не правильний) :
890.1179182848

911.4807485615 - 890.1179182848 = 21.3628302767
Різниця більша ніж допустима похибка.

Приклад даних #4

Ввід :
16 3
5
4
3

Вивід :
857.86423394

Поясненя :
5^3 = 125 = 12.8 * 9 + 9.8
4^3 =  64 = 12.8 * 5 + 0
3^3 =  27 = 12.8 * 2 + 1.4

12.8 * (9 + 5 + 2) * (4 / 3 * pi) = 857.86423394..

Вивід програми :
857.8642335182

5

Re: Algotester, задача #40427 "Новий рік"

Я не зовсім розумію, нащо тут бінарний пошук; але добре, з ним теж можна. Але в будь-якому разі сортування взагалі не потрібне. Просто задаєте ліву(0) і праву(сума всіх об'ємів) межі можливих відповідей, а далі перевіряєте, чи буде значення (ліва+права)/2 рішенням. Якщо так, це нова ліва межа, якщо ні - права, і так доти, доки права-ліва>01e-4

Подякували: leofun011

6

Re: Algotester, задача #40427 "Новий рік"

Я страшно затупив, в моєму попередньому повідомленні помилка. Програма працює досить добре.

7

Re: Algotester, задача #40427 "Новий рік"

оу, я аж тепер догнав для чого треба бінарний пошук (не для сортуваня), цікаво.

8 Востаннє редагувалося koala (03.04.2023 08:00:21)

Re: Algotester, задача #40427 "Новий рік"

Викидаємо все зайве, проходить усі тести:

#define _USE_MATH_DEFINES

#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>

const long double pi = M_PI; // 3.1415926535897932384626433832795;
double maximum(long double snow[], int children, int snowballs);

using namespace std;

int main() {
    int children = 1, snow_balls = 1;
    cin >> children >> snow_balls;
    int *snow_radius = new int[snow_balls];
    for(int i = 0; i < snow_balls; i++) {
        cin >> snow_radius[i];
    }
    long double *new_snow = new long double[snow_balls];
    for(int i = 0; i < snow_balls; i++) {
        new_snow[i] = (snow_radius[i] * snow_radius[i] * snow_radius[i] * pi * 4) / 3;
    }
    cout << fixed << setprecision(10) << maximum(new_snow, children, snow_balls) * children;
    delete[] new_snow;
    delete[] snow_radius;
    return 0;
}
double maximum(long double snow[], int children, int snow_balls) {
    long double left_bound = 0, right_bound = ((pow(100, 3) * pi * 4) / 3) + 1;
    long double mid_radius;
    int max_radius;
    double eps = 1e-7;
    while(right_bound - left_bound > eps) {
        mid_radius = (left_bound + right_bound) / 2;
        max_radius = 0;
        for(int j = 0; j < snow_balls; j++) {
            max_radius += snow[j] / mid_radius;
        }
        if(max_radius >= children)
            left_bound = mid_radius;
        else
            right_bound = mid_radius;
    }
    return left_bound;
}
Подякували: ramired, leofun012

9 Востаннє редагувалося koala (03.04.2023 07:09:19)

Re: Algotester, задача #40427 "Новий рік"

А ось доведений до пристойного вигляду з C++17:

#define _USE_MATH_DEFINES
#include <cmath>

#include <algorithm>
#include <numeric>
#include <vector>

#include <iomanip>
#include <iostream>

const double EPS = 1e-4;
double maximum(const std::vector<double> &snow, int children);

int main() {
  size_t children, snow_balls;
  std::cin >> children >> snow_balls;
  std::vector<double> snow_volume(snow_balls);
  for (double &volume : snow_volume) {
    double radius;
    std::cin >> radius;
    volume = radius * radius * radius * M_PI * 4.0 / 3.0;
  }
  std::cout << std::fixed << std::setprecision(10)
            << maximum(snow_volume, children) * children;
  return 0;
}
double maximum(const std::vector<double> &snow, int children) {
  double left_bound = 0.0,
         right_bound = *std::max_element(snow.begin(), snow.end());
  while (right_bound - left_bound >= EPS) {
    double mid_radius = (left_bound + right_bound) / 2.0;
    int max_radius =
        std::accumulate(snow.begin(), snow.end(), 0, [=](int acc, double ball) {
          return acc + ball / mid_radius;
        });
    if (max_radius >= children)
      left_bound = mid_radius;
    else
      right_bound = mid_radius;
  }
  return left_bound;
}
Подякували: ramired, leofun012