
- •Глава 2. Перебор и методы его сокращения
- •2.1. Перебор с возвратом
- •2.1.1. Общая схема
- •2.1.2. Задача о расстановке ферзей
- •2.1.3. Задача о шахматном коне
- •2.1.4. Задача о лабиринте
- •2.1.5. Задача о парламенте
- •2.1.6. Задача о рюкзаке (перебор вариантов)
- •2.1.7. Задача о коммивояжере (перебор вариантов)
- •2.1.8. Задача о секторах
- •Входные и выходные данные
- •2.2. Динамическое программирование
- •2.2.1. Задача о Черепашке
- •2.2.2. Треугольник
- •2.2.3 Степень числа
- •2.2.4. Автозаправка
- •2.2.5. Алгоритм Нудельмана-Вунша
- •2.2.6. Разбиение выпуклого n-угольника
- •2.2.7. Задача о рюкзаке (динамическая схема)
- •2.2.8. Задача о паркете
- •2.2.9. «Канадские авиалинии»
- •2.3. Метод «решета»
- •2.3.1. Решето Эратосфена
- •2.3.2. Быки и коровы
- •2.4. Задачи
2.3. Метод «решета»
Идея метода. Решето представляет собой метод комбинаторного программирования, который рассматривает конечное множество и исключает все элементы этого множества, не представляющие интереса. Он является логическим дополнением к процессу поиска с возвратом (backtrack), который перечисляет все элементы множества решений, представляющие интерес.
2.3.1. Решето Эратосфена
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
27 28 29 30 31 32
2 3 4 5 6 7 8 9 10 11 12 13 14
15 16 17 18 19 20 21 22 23 24 25
26 27 28 29 30 31 32
Числа, выделенные «жирным» шрифтом,
отбрасываются.
2 3 4 5 6 7 8 9 10 11 12
13 14 15 16 17 18 19 20 21
22 23 24 25 26 27 28 29 30
31 32
Числа, выделенные курсивом, отбрасываются.
Const N= ...;{верхняя граница интервала чисел}
Type Number=Set of 2..N;{решето, N<256}
Var S:Number;
procedure Poisk(var S:Number);
var i,j:2..N;
begin
S:=[2..N];
for i:=2 to N div 2 do
if i in S then begin
j:=i+i;
while j<=N do begin S:=S-[j];j:=j+i;end;
end;
end;
Логическим развитием задачи является ее решение при N больших 256. В этом случае необходимо ввести массив с множественным типом данных элементов.
2.3.2. Быки и коровы
(модификация задачи А.А. Суханова)
Компьютер и ребенок играют в следующую игру. Ребенок загадывает последовательность из четырех (не обязательно различных) цветов, выбранных из шести заданных. Для удобства будем обозначать цвета цифрами от 1 до 6. Компьютер должен отгадать последовательность, используя информацию, которую он получает из ответов ребенка.
Компьютер отображает на экране последовательность, а ребенок должен ответить (используя для ввода ответа клавиатуру) на два вопроса:
-
сколько правильных цветов на неправильных местах;
-
сколько правильных цветов на правильных местах.
Компьютер Ответ ребенка
1234 1 0
5156 2 1
6165 2 1
5625 1 2
5653 1 2
4655 0 4
Написать программу, которая всегда отгадывает последовательность не более чем за шесть вопросов, в худшем случае - за десять.
Правильный выбор структур данных - это уже половина решения. Итак, структуры данных и процедура их инициализации.
Const Pmax=6*6*6*6;
Type Post=String[4];
Var A:array[1..Pmax] of Post;
B:array[1..Pmax] of boolean;
cnt:integer;{счетчик числа ходов}
ok:boolean;{найдено решение}
procedure Init;
var i,j,k,l:integer;
begin
for i:=1 to 6 do
for j:=1 to 6 do
for k:=1 to 6 do
for l:=1 to 6 do A[(i-1)*216+(j-1)*36+(k-1)*6+l]:= Chr(i+Ord(‘0’))+Chr(j+Ord(‘0’))+Chr(k+Ord(‘0’))+Chr(k+Ord(‘0’));
for i:=1 to Pmax do B[i]:=true;
cnt:=0;ok:=false;
end;
Поясним на примере идею решета. Пусть длина последовательности равна двум, а количество цветов - четырем. Ребенок загадал 32, а компьютер спросил 24. Ответ ребенка 1 0, фиксируем его в переменных kr (1) и bk(0).
A |
B |
B (после анализа bk) |
B (после анализа kr) |
Результат |
11 |
true |
|
false |
false |
12 |
true |
|
|
true |
13 |
true |
|
false |
false |
14 |
true |
false |
|
false |
21 |
true |
false |
|
false |
22 |
true |
false |
|
false |
23 |
true |
false |
|
false |
24 |
true |
false |
|
false |
31 |
true |
|
false |
false |
32 |
true |
|
|
true |
33 |
true |
|
false |
false |
34 |
true |
false |
|
false |
41 |
true |
|
|
true |
42 |
true |
|
false |
false |
43 |
true |
|
|
true |
44 |
true |
false |
|
false |
Итак, было 16 возможных вопросов, после первого осталось четыре - работа решета. Функция, реализующая анализ элемента массива А, по значениям переменных kr и bk, имеет вид:
function Pr(a,b:Post;kr,bk:integer):boolean;
var i,x:integer;
begin
{проверка по “быкам”}
x:=0;
for i:=1 to 4 do if a[i]=b[i] then inc(x);
if x<>bk then begin Pr:=false;exit;end;
{проверка по “коровам”}
x:=0;
for i:=1 to 4 do if (a[i]<>b[i]) and (Pos(b[i],a)<>0) then inc(x);
if x<>kr then begin Pr:=false;exit;end;
Pr:=true;
end;
Логика - “сделать отсечение” по значению хода h.
procedure Hod(h:Post);
var i,kr,bk:integer;
begin
inc(cnt);write(cnt:2,’.’,h,’-’);
readln(kr,bk);
if bk=4 then begin ok:=true;<вывод результата>;end
else for i:=1 to Pmax do if B[i] and Not Pr(A[i],h,kr,bk) then B[i]:=false;
end;
Общая логика.
.....
begin
clrscr;
init;
hod(‘1223’);
while not ok do begin
выбор очередного хода из А
hod(h);
end;
end.
Дальнейшая работа с задачей требует ответа на следующие вопросы: