Прошу прощения за небольшой перерыв в работе
рассылки, связанный с проблемами на АТС. По
традиции;) представляем ещё одно решение задачи
из прошлого номера.
Счастливые
билеты 2 (3 уровень)
Условие:
Необходимо посчитать
количество "счастливых" билетов с заданной
суммой цифр, среди тех, номер которых состоит из
2*N разрядов. "Счастливым" является билет, у
которого сумма первых N цифр равна сумме N
последних цифр.
Технические
требования:
Во входном файле находятся два числа
разделенных пробелом: первое - N (1<=N<=50); второе -
сумма цифр интересующих нас билетов
(неотрицательное число не превосходящее 1000).
В качестве ответа необходимо вывести найденное
число "счастливых" билетов.
Пример файлов входных и выходных данных:
Решение: (by Boris Bukh <brbukh@yahoo.com>)
{$N+}
var N,S:LongInt;
{ V[i,j] - number of i-digit numbers having sum of digits=j
Recurrence: V[0,j]=delta_0j
V[i,j]=sum(k=0..9,V[i-1,j-k])
implies
V[i,j]-V[i,j-1]=V[i-1,j]-V[i-1,j-10]
Observation: V[i] is a function of V[i-1] only.
We don't need to store V[i-1].
}
V:array[0..1,-10..1000]of Comp; { we store only two consecutive rows }
i,j,k:Integer;
sel:Integer;
begin
Assign(Input,'input.txt');Reset(Input);
Assign(Output,'output.txt');Rewrite(Output);
Read(N,S);
if (S and 1)<>0 then { S has to be even }
begin
WriteLn(0);Exit;
end;
S:=S div 2;
FillChar(V,SizeOf(V),0);
sel:=0;
V[sel,0]:=1;
for i:=1 to N do
begin
FillChar(V[sel xor 1],SizeOf(V) div 2,0);
for j:=0 to S do { We have to do calculations up to S only
}
V[sel xor 1,j]:=V[sel xor 1,j-1]+V[sel,j]-V[sel,j-10];
sel:=sel xor 1;
end;
WriteLn(V[sel,S]*V[sel,S]:0:0);
end.
--------------- cut ----------------
Можно вывести формулу для V[i,j] из
рекуррентности. Обозначим через
F[i] обыкновенную производящую функцию для V[i,j] при
фиксированном i.
Тогда F[0]=1, F[i]=F[i-1]*(1+x+x^2+x^3+x^4+x^5+x^6+x^7+x^8+x^9) =>
=>F[i]=((1-x^10)/(1-x))^i=sum(x^10k*C_ik*(-1)^(i-k))*(-1)^i/(1-x)^i,
где C_ik - это биномиальный коэффициент.
Это дает нам еще один алгоритм. Мы можем
вычислить левый множитель с
помощью хорошо известного алгоритма вычисления
биномиальных
коэффициентов, а потом мы можем вычислить
частичные суммы i раз. Этот
алгоритм чуть быстрее, но не намного. Он работает
за время
(N^2/2+N*S)*(арифметика).
--------------- cut ----------------
--------------- cut ----------------
Ещё один алгоритм от Hellman'a (alexec@newmail.ru):
Нужно научиться раскладывать данное число (половину
требуемой суммы) на N слагаемых, каждое из которых
находится в интервале от 0 до 9. Дальше есть два
пути:
1. Алгоритм может сгенерировать абсолютно все
разложения (порядок слагаемых имеет значение).
Тогда мы просто считаем их количество.
2. Алгоритм генерирует все разложения, которые
нельзя получить друг из друга путем перестановок
слагаемых. Тогда при нахождении очередного
разложения надо увеличивать счетчик не на
единицу, а на
N!
---------------
k0!*k1!*...*k9!
где k0, k1, ..., k9 - количество повторяющихся чисел в
разложении.
Например, для такого разложения
1+2+2+3+3+3+3+8
получим
8!
----- = 840
2!*4!
Независимо от выбранного алгоритма в конце число
надо возвести в квадрат, т.к. мы посчитали только
количество возможных вариантов половинки билета.
Билет же целиком может состоять из любой
комбинации двух таких половинок.
--------------- cut ----------------
Реклама
в рассылке:
Рассылки проекта Sapisoft:
Всегда
рады видеть Вас на нашем сайте. Жду ваших
предложений и замечаний, Шамис Алексей