Решение
Тема: перебор, решето для поиска простых чисел
Сложность: выше среднего
Лобовое решение выглядит так:
var s,t,k,n,i:integer;
begin
read(s);
for n:=4 to sqr(s-1) do
begin
k:=0;
t:=0;
for i:=n div 2 downto 1 do
if n mod i=0 then
begin
inc(k);
t:=t+i;
if k=2 then
break;
end;
if (k=2) and (t=s) then
writeln(n);
end;
end.
В качестве кандидатов нужно рассматривать числа, не превышающие
`(S-1)^2`, так как в худшем случае составное число вида
`x^2` будет иметь два наибольших делителя
`x` и 1. Время работы этого алгоритма
`O(S^4)`. Можно существенно ускорить вычисления, если учесть, что при поиске разложения на простые множители числа
`N` можно рассмотреть только числа не превышающие
`sqrt(N)`. Наибольшим делителем числа
`N` будет
`N/a`, где
`a` – наименьший простой делитель, а вторым наибольшим делителем будет либо
`N/b`, где
`b` – второй по величине делитель
`N` (также либо простое число, либо
`a^2`), либо
`a`, если
`N` является произведением двух различных простых чисел, либо 1, если
`N=a^2`.
var s,t,k,n,i,d:integer;
begin
read(s);
for n:=4 to sqr(s-1) do
begin
k:=0;
t:=0;
d:=1;
for i:=2 to trunc(sqrt(n)) do
if n mod i=0 then
begin
inc(k);
t:=t + n div i;
if i*i<n then d:=i;
if k=2 then break;
end;
if k=1 then
begin
t:=t+d;
inc(k);
end;
if (k=2) and (t=s) then
writeln(n);
end;
end.
Время работы алгоритма уменьшилось до
`O(S^3)`, но остается достаточно большим. Существенно улучшить алгоритм, можно используя самый эффективный способ нахождения простых чисел – решето Эратосфена (вычеркивание из таблицы всех чисел, кратных простым):
const maxn=1000000;
var p:array [2..maxn] of boolean;
i,j:integer;
begin
for i:=2 to maxn do
p[i]:=true;
for i:=2 to maxn do
if p[i] then
begin
j:=i*i;
while j<=maxn do
begin
p[j]:=false;
j:=j+i;
end;
end;
end.
Для каждого числа нужно найти два наименьших делителя, поэтому массив флагов в алгоритме решета заменяем на два массива целых чисел – наименьший простой делитель и следующий по величине делитель.
const maxn=1000000;
var
s,i,i2,j:integer;
p1,p2:array[2..maxn] of integer;
begin
read(s);
for i:=2 to maxn do
begin
p1[i]:=1;
p2[i]:=i;
end;
for i:=2 to maxn do
if p1[i]=1 then { i – простое число }
begin
j:=2*i;
while j<=maxn do
begin
if p1[j]=1 then p1[j]:=i
else if p2[j]>i then p2[j]:=i;
j:=j+i;
end;
{ обработка случая, когда вторым делителем
является квадрат наименьшего делителя }
if i<=1000 then { чтобы избежать переполнения при умножении }
begin
i2:=i*i;
j:=i2;
while j<=maxn do
begin
if p2[j]>i2 then p2[j]:=i2;
j:=j+i2;
end;
end;
end
else { i – составное число }
if (i div p1[i])+(i div p2[i])=s then
writeln(i);
end.