Редакция для Корень с точностью
Submitting an official solution before solving the problem yourself is a bannable offence.
Автор:
1. Идея
Нужно найти длину стороны квадрата по его площади a.
Если сторона равна x, то площадь равна x * x. Значит, требуется найти такое число x, что:
x * x = a
То есть фактически нужно вычислить квадратный корень из a.
По условию задачу нужно решать именно вещественным двоичным поиском. Будем искать ответ на некотором отрезке [l, r], постепенно сужая его.
2. Наблюдения
Искомая длина стороны не может быть отрицательной, значит можно искать ответ среди неотрицательных чисел.
Если
a = 0, то ответ сразу равен0.Для
a > 0можно взять начальный отрезок:l = 0r = max(1, a)
Почему этого достаточно:
- если
0 < a < 1, корень мог бы быть большеa, но в нашей задачеa— целое число, так что такого случая нет; - если
a >= 1, тоsqrt(a) <= a, значит корень точно лежит в[0, a]; - для
a = 1отрезок[0, 1]тоже подходит.
Проверка середины
mочень проста:- если
m * m < a, значитmслишком маленькое, нужно двигать левую границу; - иначе
mуже не меньше корня, значит нужно двигать правую границу.
- если
Так как работаем с вещественными числами, удобно не ждать точного совпадения, а выполнить фиксированное число итераций.
В эталонном решении используется100шагов — этого более чем достаточно для точности10^(-6).
3. Алгоритм
- Считать число
a. - Если
a == 0, вывести0.0000000000. - Иначе:
- установить
l = 0.0 - установить
r = max(1, a)как вещественное число
- установить
- Повторить
100раз:m = (l + r) / 2- если
m * m < a, тоl = m - иначе
r = m
- После цикла вывести
(l + r) / 2с достаточным количеством знаков после запятой.
4. Почему это работает
Двоичный поиск поддерживает такой инвариант:
- истинный ответ всегда находится внутри отрезка
[l, r].
Сначала это верно:
l = 0r = max(1, a)- корень из
aдействительно лежит внутри этого отрезка.
Дальше на каждом шаге берём середину m.
Случай 1: m * m < a
Тогда m меньше настоящего корня, потому что квадрат числа растёт при росте неотрицательного числа.
Значит ответ лежит правее m, и можно заменить l на m.
Случай 2: m * m >= a
Тогда m не меньше настоящего корня. Значит ответ лежит не правее m, и можно заменить r на m.
В обоих случаях новый отрезок всё ещё содержит ответ, но становится в 2 раза короче.
После 100 шагов длина отрезка становится очень маленькой, поэтому любое число внутри него, например (l + r) / 2, будет приближать корень с нужной точностью.
5. Сложность
На каждом шаге делается константное число операций, а шагов ровно 100.
- Время:
O(100), то есть фактическиO(1) - Память:
O(1)
6. Код на C++17
#include <iostream>
#include <iomanip>
#include <algorithm>
using namespace std;
int main() {
long long a;
cin >> a;
if (a == 0) {
cout << fixed << setprecision(10) << 0.0 << "\n";
return 0;
}
double l = 0.0;
double r = (double)max(1LL, a);
for (int i = 0; i < 100; i++) {
double m = (l + r) / 2.0;
if (m * m < (double)a) {
l = m;
} else {
r = m;
}
}
cout << fixed << setprecision(10) << (l + r) / 2.0 << "\n";
return 0;
}
7. Код на Python 3
a = int(input())
if a == 0:
print("0.0000000000")
else:
l = 0.0
r = float(max(1, a))
for _ in range(100):
m = (l + r) / 2.0
if m * m < a:
l = m
else:
r = m
print(f"{(l + r) / 2.0:.10f}")
Комментарии