Здравствуйте, уважаемые подписчики.
Долго был занят, поэтому задержался с выпуском. Я и сейчас занят (с
завтрашнего дня :-), точнее, уже с сегодняшнего) моим новым проектом, но,
забив на всё остальное, готовлю выпуск.
В прошлом выпуске поступил очень опрометчиво - дал текст задачи, даже не
подумав над ней, не то что решив. В результате, как правильно заметили
некоторые читатели, на олимпиадную она не потянула - слишком расплывчаты
условия и ошибка в описании (девятка вместо "e"). Если кого это отпугнуло
от решения, уточню заново условие.
Вводится строка типа
a?b?c?f?e=zz
где
a,b,c,....,z = числа типа byte, количество не превышает 20
zz = контрольный результат, умещается в типе Longint (как и все
промежуточные результаты).
Требуется определить, что из множества (+,*,-,div) требуется подставить
вместо знака ?, чтобы получить верный пример. (Именно верный, с учётом
приоритета операций). Вывести все возможные варианты.
пример 3?2?1=6
вывод
3+2+1=6
3*2*1=6
3*2 div 1=6
Полагаю, многим было бы интересно авторское решение, но боясь, как бы меня
не обвинили в плагиате :-), приводить его не буду. Типичный перебор,
ничего больше (желающие уже могут начинать кидать камни). Приведу лучше
решение Dmitry Akulov. Первое - это приведённое мной к виду, который можно
скомпилировать в BP 7.0 (в оригинале комментариев тоже не было, с целью
уменьшения размеров :-) ), второе - это с оригинальными комментариями
автора решения (он прислал 2 варианта, с комментариями и без), но не
приведённое (оригинал - Delphi).
Да большая просьба - пишите то, что можно скомпилировать в BP 7.0. Да, я
могу преобразовать дельфийский код, дописать несуществующие в BP функции и
пр., но всё же. Если хотите, можно изменить направленность рассылки.
Перейти в среду Delphi. Или сменить язык на с++ или даже asm. Просьба
написать все свои соображения по этому поводу.
Кстати, судя по количеству присланных решений (чуть ли не на порядок
меньше, чем на прошлую задачу), сложность этой - именно то, что нужно.
Сложнее видимо не стоит предлагать. Да, на следующий раз пока не предлагаю
задачу - решу сначала сам, потом вышлю отдельным выпуском.
И последнее. 2 просьбы - не надо мне присылать скомпилированные
решения - скомпилировать я и сам могу и не надо в целях уменьшения размера
убивать в решении оформление - оно, конечно, короткое, но читать намного
труднее.
const
OP_ADD = 1;
OP_SUB = 2;
OP_MUL = 3;
OP_DIV = 4;
opchars:array[1..4] of string =
(' + ',' - ',' * ',' div ');
type
intarray = array[1..128] of integer;
var
nums,ops:intarray;
res,nc,oc:integer;
s:string;
procedure init;
var i:integer;
t:string;
cod:integer;
begin
t:=''; nc:=1; oc:=1;
for i:=1 to length(s) do
begin
if (s[i]='?') or (s[i]='=') then
begin
val(t,nums[nc],cod);
Inc(nc);
Inc(oc);
t:='';
end else t:=t+s[i];
end;
val(t,res,cod);
Dec(nc);
Dec(oc,2);
end;
function calc:integer;
var i,sum:integer;
oldnums,oldops:intarray;
begin
oldnums:=nums;
oldops:=ops;
for i:=1 to oc do
begin
if ops[i]=OP_SUB then
begin
ops[i]:=OP_ADD;
nums[i+1]:=-nums[i+1];
end;
if ops[i]>OP_SUB then
begin
if ops[i]=OP_MUL then
nums[i+1]:=nums[i]*nums[i+1] else
nums[i+1]:=nums[i] div nums[i+1];
ops[i]:=OP_ADD;
nums[i]:=0;
end;
end;
sum:=0;
for i:=1 to nc do Inc(sum,nums[i]);
nums:=oldnums;
ops:=oldops;
calc:=sum;
end;
procedure next(k:integer);
var i,r:integer;
begin
if k>oc then
begin
r:=calc;
if r=res then
begin
for i:=1 to oc do
write(nums[i],opchars[ops[i]]);
writeln(nums[nc],' = ',r);
end;
end else
begin
for i:=OP_ADD to OP_DIV do
begin
ops[k]:=i;
next(k+1);
end;
end;
end;
begin
write('s = '); readln(s);
init;
next(1);
writeln('end');
readln;
end.
с комментариями:
const
OP_ADD = 1; // +
OP_SUB = 2; // -
OP_MUL = 3; // *
OP_DIV = 4; // div
opchars:array[1..4] of string = (' + ',' - ',' * ',' div ');
type
intarray = array[1..128] of integer;
var
numbers,operations:intarray;
res,numcount,opcount,varcount:integer;
s:string;
procedure PrepareString;
var i:integer;
t:string;
begin
t:=''; numcount:=1; opcount:=1;
for i:=1 to length(s) do
begin
if (s[i]='?') or (s[i]='=') then // если встретили ? или =
// то начинается новое число
begin
numbers[numcount]:=StrToInt(t);
// записываем старое число в массив
Inc(numcount);
Inc(opcount);
t:='';
end else t:=t+s[i]; //
end;
res:=StrToInt(t); //то что осталось после = - это то что должно
получится
Dec(numcount);
Dec(opcount,2);
end;
function CalcString:integer;
var i,sum:integer;
oldnumbers,oldoperations:intarray;
begin
{
Вычисление значения строки с учетом порядка действий
Основная идея в том, чтобы преобразовать последовательность любых
действий в последовательность только сложений, такое можно достичь
следующим образом: a+b*c = a+0+b*c
Например, 2+3*4 = 2+0+(3*4) = 2+0+12 = 14
Т.е. часть выражения 3*4 мы преобразовали в сумму 0+12
}
oldnumbers:=numbers; // сохранение старых данных нужно, т.к.
oldoperations:=operations; // числа и действия могут измениться
for i:=1 to opcount do
begin
if operations[i]=OP_SUB then // преобразование вычитания в
сложение
// a-b = a+(-b)
begin
operations[i]:=OP_ADD;
numbers[i+1]:=-numbers[i+1];
end;
if operations[i]>OP_SUB then // если встретили умножение или
деление
begin
if operations[i]=OP_MUL then
numbers[i+1]:=numbers[i]*numbers[i+1] else
numbers[i+1]:=numbers[i] div numbers[i+1];
// то преобразовываем в сложение
operations[i]:=OP_ADD;
numbers[i]:=0;
end;
end;
// подсчет суммы полученной последовательности чисел
sum:=0;
for i:=1 to numcount do Inc(sum,numbers[i]);
numbers:=oldnumbers; // восстановление исходных данных
operations:=oldoperations;
CalcString:=sum;
end;
procedure ChooseNextOp(k:integer);
var i,r:integer;
begin
if k>opcount then
begin
// если выбрали все действия, то подсчитываем результат
// и сравниваем с нужным
r:=CalcString;
if r=res then
begin
Inc(varcount);
// распечатка полученной цепочки действий
write('Variant ',varcount,': ');
for i:=1 to opcount do
write(numbers[i],opchars[operations[i]]);
writeln(numbers[numcount],' = ',r);
end;
end else
begin
// выбираем дейсвие
for i:=OP_ADD to OP_DIV do
begin
operations[k]:=i;
ChooseNextOp(k+1); // и переходим к выбору след. действия
end;
end;
end;
begin
write('Enter mask: '); readln(s); writeln;
PrepareString; // подготовка данных
varcount:=0;
ChooseNextOp(1); // выбираем действия
// распечатка количества вариантов
if varcount>0 then
begin
writeln;
writeln('Total: ',varcount,' variants');
// Всего N вариантов
end
else writeln('No variants found');
// Варианты не найдены
writeln;
writeln('Press ENTER to exit...');
readln;
end.