Tải bản đầy đủ (.pdf) (258 trang)

chuyên đề bài toán liệt kê

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (5.67 MB, 258 trang )


Bài toán lit kê
Lê Minh Hoàng
{ 1 z
MC LC
§
0. GI
I THIU
2
§
1. NH
C LI MT S KIN THC I S T HP
3
I. CH
NH HP LP
3
II. CH
NH HP KHÔNG LP
3
III. HOÁN V

3
IV. T
 HP
3
§
2. PH
NG PHÁP SINH (GENERATE)
5
I. SINH CÁC DÃY NH


 PHÂN  DÀI N
6
II. LI
T KÊ CÁC TP CON K PHN T
7
III. LI
T KÊ CÁC HOÁN V
9
§
3. THU
T TOÁN QUAY LUI
12
I. LI
T KÊ CÁC DÃY NH PHÂN  DÀI N
13
II. LI
T KÊ CÁC TP CON K PHN T
14
III. LI
T KÊ CÁC CHNH HP KHÔNG LP CHP K
15
IV. BÀI TOÁN PHÂN TÍCH S

16
V. BÀI TOÁN X
P HU
18
§
4. K
 THUT NHÁNH CN

22
I. BÀI TOÁN T
I U
22
II. S
 BÙNG N T HP
22
III. MÔ HÌNH K
 THUT NHÁNH CN
22
IV. BÀI TOÁN NG
I DU LCH
23
V. DÃY ABC 25
Bài toán lit kê
Lê Minh Hoàng
{ 2 z
§
0. GII THIU
Trong thc t, có mt s bài toán yêu cu ch rõ: trong mt tp các đi tng cho trc có bao
nhiêu đi tng tho mãn nhng điu kin nht đnh. Bài toán đó gi là bài toán đm cu hình t
hp.
Trong lp các bài toán đm, có nhng bài toán còn yêu cu ch rõ nhng cu hình tìm đc tho
mãn điu kin đã cho là nhng cu hình nào. Bài toán yêu cu đa ra danh sách các cu hình có th
có gi là bài toán lit kê t hp.
 gii bài toán lit kê, cn phi xác đnh đc mt thut toán đ có th theo đó ln lt xây dng
đc tt c các cu hình đang quan tâm. Có nhiu phng pháp lit kê, nhng chúng cn phi đáp
ng đc hai yêu cu di đây:
• Không đc lp li mt cu hình
• Không đc b sót mt cu hình

Có th nói rng, phng pháp lit kê là phng k cui cùng đ gii đc mt s bài toán t hp
hin nay. Khó khn chính ca phng pháp này chính là s bùng n t hp.  xây dng 1 t cu
hình (con s này không phi là ln đi vi các bài toán t hp - Ví d lit kê các cách xp n≥13
ngi quanh mt bàn tròn) và gi thit rng mi thao tác xây dng mt khong 1 giây, ta phi mt
quãng 31 nm mi gii xong. Tuy nhiên cùng vi s phát trin ca máy tính đin t, bng phng
pháp lit kê, nhiu bài toán t hp đã tìm thy li gii. Qua đó, ta cng nên bit rng ch nên dùng
phng pháp lit kê khi không còn mt phng pháp nào khác tìm ra li gii. Chính nhng n
lc gii quyt các bài toán thc t không dùng phng pháp lit kê đã thúc đy s phát trin ca
nhiu ngành toán hc.
Cui cùng, nhng tên gi sau đây, tuy v ngha không phi đng nht, nhng trong mt s trng
hp ngi ta có th dùng ln ngha ca nhau đc. ó là:
• Phng pháp lit kê
• Phng pháp vét cn trên tp phng án
• Phng pháp duyt toàn b
Bài toán lit kê
Lê Minh Hoàng
{ 3 z
§
1. NHC LI MT S KIN THC I S T HP
Cho S là mt tp hu hn gm n phn t và k là mt s t nhiên.
Gi X là tp các s nguyên dng t 1 đn k: X = {1, 2, , k}
I. CHNH HP LP
Mi ánh x f: X → S. Cho tng ng vi mi i ∈ X, mt và ch mt phn t f(i) ∈ S.
c gi là mt chnh hp lp chp k ca S.
Nhng do X là tp hu hn (k phn t) nên ánh x f có th xác đnh qua bng các giá tr f(1), f(2),
, f(k).
Ví d: S = {A, B, C, D, E, F}; k = 3. Mt ánh x f có th cho nh sau:
i 123
f(i) E C E
Nên ngi ta đng nht f vi dãy giá tr (f(1), f(2), , f(k)) và coi dãy giá tr này cng là mt

chnh hp lp chp k ca S. Nh ví d trên (E, C, E) là mt chnh hp lp chp 3 ca S. D dàng
chng minh đc kt qu sau bng quy np hoc bng phng pháp đánh giá kh nng la chn:
S chnh hp lp chp k ca tp gm n phn t:
k
k
n
nA =
II. CHNH HP KHÔNG LP
Khi f là đn ánh có ngha là vi ∀i, j ∈ X ta có f(i) = f(j) ⇔ i = j. Nói mt cách d hiu, khi dãy giá
tr f(1), f(2), , f(k) gm các phn t thuc S khác nhau đôi mt thì f đc gi là mt chnh hp
không lp chp k ca S. Ví d mt chnh hp không lp (C, A, E):
i 123
f(i) C A E
S chnh hp không lp chp k ca tp gm n phn t:
)!kn(
!n
)1kn) (2n)(1n(nA
k
n

=+−−−=
III. HOÁN V
Khi k = n. Mt chnh hp không lp chp n ca S đc gi là mt hoán v các phn t ca S.
Ví d: mt hoán v: (A, D, C, E, B, F) ca S = {A, B, C, D, E, F}
i123456
f(i) A D C E B F
 ý rng khi k = n thì s phn t ca tp X = {1, 2, , n} đúng bng s phn t ca S. Do tính cht
đôi mt khác nhau nên dãy f(1), f(2), , f(n) s lit kê đc ht các phn t trong S. Nh vy f là
toàn ánh. Mt khác do gi thit f là chnh hp không lp nên f là đn ánh. Ta có tng ng 1-1 gia
các phn t ca X và S, do đó f là song ánh. Vy nên ta có th đnh ngha mt hoán v ca S là mt

song ánh gia {1, 2, , n} và S.
S hoán v ca tp gm n phn t = s chnh hp không lp chp n:
!nP
n
=
IV. T HP
Mt tp con gm k phn t ca S đc gi là mt t hp chp k ca S.
Bài toán lit kê
Lê Minh Hoàng
{ 4 z
Ly mt tp con k phn t ca S, xét tt c k! hoán v ca tp con này. D thy rng các hoán v đó
là các chnh hp không lp chp k ca S. Ví d ly tp {A, B, C} là tp con ca tp S trong ví d
trên thì: (A, B, C), (C, A, B), (B, C, A), là các chnh hp không lp chp 3 ca S. iu đó tc là
khi lit kê tt c các chnh hp không lp chp k thì mi t hp chp k s đc tính k! ln. Vy:
S t hp chp k ca tp gm n phn t:
)!kn(!k
!n
!k
A
C
k
n
k
n

==
S tp con ca tp n phn t:

nnn
n

1
n
0
n
2)11(C CC
=+=+++
Bài toán lit kê
Lê Minh Hoàng
{ 5 z
§
2. PHNG PHÁP SINH (GENERATE)
Phng pháp sinh có th áp dng đ gii bài toán lit kê t hp đt ra nu nh hai điu kin sau
tho mãn:
1. Có th xác đnh đc mt th t trên tp các cu hình t hp cn lit kê. T đó có th xác
đnh đc cu hình đu tiên và cu hình cui cùng trong th t đã xác đnh
2. Xây dng đc thut toán t cu hình cha phi cu hình cui, sinh ra đc cu hình k
tip nó.
Phng pháp sinh có th mô t nh sau:
<Xây dng cu hình đu tiên>;
repeat
<a ra cu hình đang có>;
<T cu hình đang có sinh ra cu hình k tip nu còn>;
until <ht cu hình>;
Th t t đin
Trên các kiu d liu đn gin chun, ngi ta thng nói ti khái nim th t. Ví d trên kiu s
thì có quan h: 1 < 2; 2 < 3; 3 < 10; , trên kiu ký t Char thì cng có quan h 'A' < 'B'; 'C' < 'c'
Xét quan h th t toàn phn "nh hn hoc bng" ký hiu "≤" trên mt tp hp S, là quan h hai
ngôi tho mãn bn tính cht:
Vi ∀a, b, c ∈ S
• Tính ph bin: Hoc là a ≤ b, hoc b ≤ a;

• Tính phn x: a ≤ a
• Tính phn đi xng: Nu a ≤ b và b ≤ a thì bt buc a = b.
• Tính bc cu: Nu có a ≤ b và b ≤ c thì a ≤ c.
Trong trng hp a ≤ b và a ≠ b, ta dùng ký hiu "<" cho gn, (ta ngm hiu các ký hiu nh ≥, >,
khi phi đnh ngha)
Ví d nh quan h "≤" trên các s nguyên cng nh trên các kiu vô hng, lit kê là quan h th t
toàn phn.
Trên các dãy hu hn, ngi ta cng xác đnh mt quan h th t:
Xét a = (a
1
, a
2
, , a
n
) và b = (b
1
, b
2
, , b
n
); trên các phn t ca a
1
, , a
n
, b
1
, , b
n
đã có quan h
th t "≤". Khi đó a ≤ b nu nh

• Hoc a
i
= b
i
vi ∀i: 1 ≤ i ≤ n.
• Hoc tn ti mt s nguyên dng k: 1 ≤ k < n đ:
a
1
= b
1
a
2
= b
2

a
k-1
= b
k-1
a
k
= b
k
a
k+1
< b
k+1
Trong trng hp này, ta có th vit a < b.
Th t đó gi là th t t đin trên các dãy đ dài n.
Khi đ dài hai dãy a và b không bng nhau, ngi ta cng xác đnh đc th t t đin. Bng cách

thêm vào cui dãy a hoc dãy b nhng phn t đc bit gi là phn t ∅ đ đ dài ca a và b bng
Bài toán lit kê
Lê Minh Hoàng
{ 6 z
nhau, và coi nhng phn t ∅ này nh hn tt c các phn t khác, ta li đa v xác đnh th t t
đin ca hai dãy cùng đ dài. Ví d:
• (1, 2, 3, 4) < (5, 6)
• (a, b, c) < (a, b, c, d)
• 'calculator' < 'computer'
I. SINH CÁC DÃY NH PHÂN Đ DÀI N
Mt dãy nh phân đ dài n là mt dãy x = x
1
x
2
x
n
trong đó x
i
∈ {0, 1} (∀i : 1 ≤ i ≤ n).
D thy: mt dãy nh phân x đ dài n là biu din nh phân ca mt giá tr nguyên p(x) nào đó nm
trong đon [0, 2
n
- 1]. S các dãy nh phân đ dài n = s các s nguyên ∈ [0, 2
n
- 1] = 2
n
. Ta s lp
chng trình lit kê các dãy nh phân theo th t t đin có ngha là s lit kê ln lt các dãy nh
phân biu din các s nguyên theo th t 0, 1, , 2
n

-1.
Ví d: Khi n = 3, các dãy nh phân đ dài 3 đc lit kê nh sau:
p(x)01234567
x 000 001 010 011 100 101 110 111
Nh vy dãy đu tiên s là 00 0 và dãy cui cùng s là 11 1. Nhn xét rng nu dãy x = (x
1
, x
2
, ,
x
n
) là dãy đang có và không phi dãy cui cùng thì dãy k tip s nhn đc bng cách cng thêm 1
( theo c s 2 có nh) vào dãy hin ti.
Ví d khi n = 8:
Dãy đang có: 10010000
Dãy đang có: 10010111
cng thêm 1: + 1 cng thêm 1: + 1
Dãy mi: 10010001 Dãy mi: 10011000
Nh vy k thut sinh cu hình k tip t cu hình hin ti có th mô t nh sau: Xét t cui
dãy v đu (xét t hàng đn v lên), gp s 0 đu tiên thì thay nó bng s 1 và đt tt c các phn
t phía sau v trí đó bng 0.
i := n;
while (i > 0) and (x
i
= 1) do i := i - 1;
if i > 0 then
begin
x
i
:= 1;

for j := i + 1 to n do x
j
:= 0;
end;
D liu vào (Input): nhp t file vn bn BSTR.INP cha s nguyên dng n ≤ 30
Kt qu ra(Output): ghi ra file vn bn BSTR.OUT các dãy nh phân đ dài n.
BSTR.INP BSTR.OUT
3 000
001
010
011
100
101
110
111
PROG02_1.PAS * Thut toán sinh lit kê các dãy nh phân đ dài n
program Binary_Strings;
const
max = 30;
Bài toán lit kê
Lê Minh Hoàng
{ 7 z
var
x: array[1 max] of Integer;
n, i: Integer;
begin

{nh ngha li thit b nhp/xut chun}
Assign(Input, 'BSTR.INP'); Reset(Input);
Assign(Output, 'BSTR.OUT'); Rewrite(Output);

ReadLn(n);
FillChar(x, SizeOf(x), 0);
{C
u hình ban đu x
1
= x
2
= = x
n
:= 0}
repeat
{Thu
t toán sinh}
for i := 1 to n do Write(x[i]);
{In ra c
u hình hin ti}
WriteLn;
i := n;
{x
i
là ph
n t cui dãy, lùi dn i cho ti khi gp s 0 hoc khi i = 0 thì dng}
while (i > 0) and (x[i] = 1) do Dec(i);
if i > 0 then
{Ch
a gp phi cu hình 11 1}
begin
x[i] := 1;
{Thay x
i

b
ng s 1}
FillChar(x[i + 1], (n - i) * SizeOf(x[1]), 0); {t x
i + 1
= x
i + 2
= = x
n
:= 0}
end;
until i = 0; {ã ht cu hình}

{óng thit b nhp xut chun, thc ra không cn vì BP s t đng đóng Input và Output trc khi thoát chng trình}
Close(Input); Close(Output);
end.
II. LIT KÊ CÁC TP CON K PHN T
Ta s lp chng trình lit kê các tp con k phn t ca tp {1, 2, , n} theo th t t đin
Ví d: vi n = 5, k = 3, ta phi lit kê đ 10 tp con:
1.{1, 2, 3} 2.{1, 2, 4} 3.{1, 2, 5} 4.{1, 3, 4} 5.{1, 3, 5}
6.{1, 4, 5} 7.{2, 3, 4} 8.{2, 3, 5} 9.{2, 4, 5} 10.{3, 4, 5}
Nh vy tp con đu tiên (cu hình khi to) là {1, 2, , k}.
Cu hình kt thúc là {n - k + 1, n - k + 2, , n}.
Nhn xét: Ta s in ra tp con bng cách in ra ln lt các phn t ca nó theo th t tng dn. T
đó, ta có nhn xét nu x = {x
1
, x
2
, , x
k
} và x

1
< x
2
< < x
k
thì gii hn trên (giá tr ln nht có
th nhn) ca x
k
là n, ca x
k-1
là n - 1, ca x
k-2
là n - 2
C th: gii hn trên ca x
i
= n - k + i;
Còn tt nhiên, gii hn di ca x
i
(giá tr nh nht x
i
có th nhn) là x
i-1
+ 1.
Nh vy nu ta đang có mt dãy x đi din cho mt tp con, nu x là cu hình kt thúc có ngha là
tt c các phn t trong x đu đã đt ti gii hn trên thì quá trình sinh kt thúc, nu không thì ta
phi sinh ra mt dãy x mi tng dn tho mãn va đ ln hn dãy c theo ngha không có mt tp
con k phn t nào chen gia chúng khi sp th t t đin.
Ví d: n = 9, k = 6. Cu hình đang có x = {1, 2, 6, 7, 8, 9}. Các phn t x
3
đn x

6
đã đt ti gii
hn trên nên đ sinh cu hình mi ta không th sinh bng cách tng mt phn t trong s các x
6
, x
5
,
x
4
, x
3
lên đc, ta phi tng x
2
= 2 lên thành x
2
= 3. c cu hình mi là x = {1, 3, 6, 7, 8, 9}. Cu
hình này đã tho mãn ln hn cu hình trc nhng cha tho mãn tính cht va đ ln mun vy
ta li thay x
3
, x
4
, x
5
, x
6
bng các gii hn di ca nó. Tc là:
• x
3
:= x
2

+ 1 = 4
• x
4
:= x
3
+ 1 = 5
• x
5
:= x
4
+ 1 = 6
• x
6
:= x
5
+ 1 = 7
Ta đc cu hình mi x = {1, 3, 4, 5, 6, 7} là cu hình k tip. Nu mun tìm tip, ta li nhn thy
rng x
6
= 7 cha đt gii hn trên, nh vy ch cn tng x
6
lên 1 là đc x = {1, 3, 4, 5, 6, 8}.
Vy k thut sinh tp con k tip t tp đã có x có th xây dng nh sau:
Bài toán lit kê
Lê Minh Hoàng
{ 8 z
• Tìm t cui dãy lên đu cho ti khi gp mt phn t x
i
cha đt gii hn trên n - k + i.
i := n;

while (i > 0) and (x
i
= n - k + i) do i := i - 1;
(1, 2, 6, 7, 8, 9);
• Nu tìm thy:
if i > 0 then
♦ Tng x
i
đó lên 1.
x
i
:= x
i
+ 1;
(1, 3, 6, 7, 8, 9)
♦ t tt c các phn t phía sau x
i
bng gii hn di:
for j := i + 1 to k do x
j
:= x
j-1
+ 1;
(1, 3, 4, 5, 6, 7)
Input: file vn bn SUBSET.INP cha hai s nguyên dng n, k (1 ≤ k ≤ n ≤ 30) cách nhau ít nht
mt du cách
Output: file vn bn SUBSET.OUT các tp con k phn t ca tp {1, 2, , n}
SUBSET.INP SUBSET.OUT
5 3 {1, 2, 3}
{1, 2, 4}

{1, 2, 5}
{1, 3, 4}
{1, 3, 5}
{1, 4, 5}
{2, 3, 4}
{2, 3, 5}
{2, 4, 5}
{3, 4, 5}
PROG02_2.PAS * Thut toán sinh lit kê các tp con k phn t
program Combinations;
const
max = 30;
var
x: array[1 max] of Integer;
n, k, i, j: Integer;
begin

{nh ngha li thit b nhp/xut chun}
Assign(Input, 'SUBSET.INP'); Reset(Input);
Assign(Output, 'SUBSET.OUT'); Rewrite(Output);
ReadLn(n, k);
for i := 1 to k do x[i] := i;
{x
1
:= 1; x
2
:= 2; ; x
3
:= k (C
u hình khi to)}

Count := 0;
{Bi
n đm}
――repeat

{In ra c
u hình hin ti}
Write('{');
for i := 1 to k - 1 do Write(x[i], ', ');
WriteLn(x[k], '}');

{Sinh ti
p}
i := k;
{x
i
là ph
n t cui dãy, lùi dn i cho ti khi gp mt x
i
ch
a đt gii hn trên n - k + i}
while (i > 0) and (x[i] = n - k + i) do Dec(i);
if i > 0 then―
{N
u cha lùi đn 0 có ngha là cha phi cu hình kt thúc}
―― begin
Inc(x[i]); {Tng x
i
lên 1,
t các phn t đng sau x

i
b
ng gii hn di ca nó}
for j := i + 1 to k do x[j] := x[j - 1] + 1;
end;
until i = 0;
{Lùi
đn tn 0 có ngha là tt c các phn t đã đt gii hn trên - ht cu hình}
Close(Input); Close(Output);
end.
Bài toán lit kê
Lê Minh Hoàng
{ 9 z
III. LIT KÊ CÁC HOÁN V
Ta s lp chng trình lit kê các hoán v ca {1, 2, , n} theo th t t đin.
Ví d vi n = 4, ta phi lit kê đ 24 hoán v:
1.1234 2.1243 3.1324 4.1342 5.1423 6.1432
7.2134 8.2143 9.2314 10.2341 11.2413 12.2431
13.3124 14.3142 15.3214 16.3241 17.3412 18.3421
19.4123 20.4132 21.4213 22.4231 23.4312 24.4321
Nh vy hoán v đu tiên s là (1, 2, , n). Hoán v cui cùng là (n, n-1, , 1).
Hoán v s sinh ra phi ln hn hoán v hin ti, hn th na phi là hoán v va đ ln hn hoán v
hin ti theo ngha không th có mt hoán v nào khác chen gia chúng khi sp th t.
Gi s hoán v hin ti là x = (3, 2, 6, 5, 4, 1), xét 4 phn t cui cùng, ta thy chúng đc xp gim
dn, điu đó có ngha là cho dù ta có hoán v 4 phn t này th nào, ta cng đc mt hoán v bé
hn hoán v hin ti!. Nh vy ta phi xét đn x
2
= 2, thay nó bng mt giá tr khác. Ta s thay bng
giá tr nào?, không th là 1 bi nu vy s đc hoán v nh hn, không th là 3 vì đã có x
1

= 3 ri
(phn t sau không đc chn vào nhng giá tr mà phn t trc đã chn). Còn li các giá tr 4, 5,
6. Vì cn mt hoán v va đ ln hn hin ti nên ta chn x
2
= 4. Còn các giá tr (x
3
, x
4
, x
5
, x
6
) s
ly trong tp {2, 6, 5, 1}. Cng vì tính va đ ln nên ta s tìm biu din nh nht ca 4 s này gán
cho x
3
, x
4
, x
5
, x
6
tc là (1, 2, 5, 6). Vy hoán v mi s là (3, 4, 1, 2, 5, 6).
(3, 2, 6, 5, 4, 1) → (3, 4, 1, 2, 5, 6).
Ta có nhn xét gì qua ví d này: on cui ca hoán v đc xp gim dn, s x
5
= 4 là s nh nht
trong đon cui gim dn tho mãn điu kin ln hn x
2
= 2. Nu đi ch x

5
cho x
2
thì ta s đc x
2
= 4 và đon cui vn đc sp xp gim dn. Khi đó mun biu din nh nht cho các giá tr
trong đon cui thì ta ch cn đo ngc đon cui.
Trong trng hp hoán v hin ti là (2, 1, 3, 4) thì hoán v k tip s là (2, 1, 4, 3). Ta cng có th
coi hoán v (2, 1, 3, 4) có đon cui gim dn, đon cui này ch gm 1 phn t (4)
Vy k thut sinh hoán v k tip t hoán v hin ti có th xây dng nh sau:
• Xác đnh đon cui gim dn dài nht, tìm ch s i ca phn t x
i
đng lin trc đon cui đó.
iu này đng ngha vi vic tìm t v trí sát cui dãy lên đu, gp ch s i đu tiên tha mãn x
i
< x
i+1
. Nu toàn dãy đã là gim dn, thì đó là cu hình cui.
i := n - 1;
while (i > 0) and (x
i
> x
i+1
) do i := i - 1;
• Trong đon cui gim dn, tìm phn t x
k
nh nht tho mãn điu kin x
k
> x
i

. Do đon cui
gim dn, điu này thc hin bng cách tìm t cui dãy lên đu gp ch s k đu tiên tho mãn
x
k
> x
i
(có th dùng tìm kim nh phân).
k := n;
while x
k
< x
i
do k := k - 1;
• i ch x
k
và x
i
, lt ngc th t đon cui gim dn (t x
i+1
đn x
k
) tr thành tng dn.
Input: file vn bn PERMUTE.INP cha s nguyên dng n ≤ 12
Output: file vn bn PERMUTE.OUT các hoán v ca dãy (1, 2, , n)
PERMUTE.INP PERMUTE.OUT
3 1 2 3
1 3 2
2 1 3
2 3 1
3 1 2

3 2 1
Bài toán lit kê
Lê Minh Hoàng
{ 10 z
PROG02_3.PAS * Thut toán sinh lit kê hoán v
program Permute;
const
max = 12;
var
n, i, k, a, b: Integer;
x: array[1 max] of Integer;
procedure Swap(var X, Y: Integer);
{Th
 tc đo giá tr hai tham bin X, Y}
var
Temp: Integer;
begin
Temp := X; X := Y; Y := Temp;
end;
begin
Assign(Input, 'PERMUTE.INP'); Reset(Input);
Assign(Output, 'PERMUTE.OUT'); Rewrite(Output);
ReadLn(n);
for i := 1 to n do x[i] := i;

{Kh
i to cu hình đu: x
1
:= 1; x
2

:= 2; , x
n
:= n}
――
repeat
――――
for i := 1 to n do Write(x[i], ' ');
{In ra c
u hình hoán v hin ti}
WriteLn;
i := n - 1;
while (i > 0) and (x[i] > x[i + 1]) do Dec(i);
if i > 0 then
{Ch
a gp phi hoán v cui (n, n-1, ,1)}
――――――
begin
k := n;
{x
k
là ph
n t cui dãy}
――――
while x[k] < x[i] do Dec(k);

{Lùi d
n k đ tìm gp x
k
đu tiên ln hn x
i

}
Swap(x[k], x[i]);
{i ch x
k
và x
i
}
――――――
a := i + 1; b := n;
{L
t ngc đon cui gim dn, a: đu đon, b: cui đon}
――
while a < b do
begin
Swap(x[a], x[b]); {i ch x
a
và x
b
}
Inc(a);
{Ti
n a và lùi b, đi ch tip cho ti khi a, b chm nhau}
Dec(b);
end;
end;
until i = 0;

{Toàn dãy là dãy gi
m dn - không sinh tip đc - ht cu hình}
Close(Input); Close(Output);

end.
Bài tp:
1. Các chng trình trên x lý không tt trong trng hp tm thng, đó là trng hp n = 0 đi
vi chng trình lit kê dãy nh phân cng nh trong chng trình lit kê hoán v, trng hp k = 0
đi vi chng trình lit kê t hp, hãy khc phc điu đó.
2. Lit kê các dãy nh phân đ dài n có th coi là lit kê các chnh hp lp chp n ca tp 2 phn t
{0, 1}. Hãy lp chng trình:
Nhp vào hai s n và k, lit kê các chnh hp lp chp k ca {0, 1, , n -1}.
Gi ý: thay h c s 2 bng h c s n.
3. Hãy lit kê các dãy nh phân đ dài n mà trong đó cm ch s "01" xut hin đúng 2 ln.
Bài tp:
4. Nhp vào mt danh sách n tên ngi. Lit kê tt c các cách chn ra đúng k ngi trong s n
ngi đó.
Gi ý: xây dng mt ánh x t tp {1, 2, , n} đn tp các tên ngi. Ví d xây dng mt mng
Tên: Tên[1] := 'Nguyn vn A'; Tên[2] := 'Trn th B'; sau đó lit kê tt c các tp con k phn t
Bài toán lit kê
Lê Minh Hoàng
{ 11 z
ca tp {1, 2, , n}. Ch có điu khi in tp con, ta không in giá tr s {1, 3, 5} mà thay vào đó s in
ra {Tên[1], Tên [3], Tên[5]}. Tc là in ra nh ca các giá tr tìm đc qua ánh x
5. Lit kê tt c các tp con ca tp {1, 2, , n}. Có th dùng phng pháp lit kê tp con nh trên
hoc dùng phng pháp lit kê tt c các dãy nh phân. Mi s 1 trong dãy nh phân tng ng vi
mt phn t đc chn trong tp. Ví d vi tp {1, 2, 3, 4} thì dãy nh phân 1010 s tng ng vi
tp con {1, 3}. Hãy lp chng trình in ra tt c các tp con ca {1, 2, , n} theo hai phng pháp.
5. Nhp vào danh sách tên n ngi, in ra tt c các cách xp n ngi đó vào mt bàn
6. Nhp vào danh sách n ngi nam và n ngi n, in ra tt c các cách xp 2n ngi đó vào mt
bàn tròn, mi ngi nam tip đn mt ngi n.
7. Ngi ta có th dùng phng pháp sinh đ lit kê các chnh hp không lp chp k. Tuy nhiên có
mt cách là lit kê tt c các tp con k phn t ca tp hp, sau đó in ra đ k! hoán v ca nó. Hãy
vit chng trình lit kê các chnh hp không lp chp k ca {1, 2, , n}.

8. Lit kê tt c các hoán v ch cái trong t MISSISSIPPI theo th t t đin.
9. Lit kê tt c các cách phân tích s nguyên dng n thành tng các s nguyên dng, hai cách
phân tích là hoán v ca nhau ch tính là mt cách.
Cui cùng, ta có nhn xét, mi phng pháp lit kê đu có u, nhc đim riêng và phng pháp
sinh cng không nm ngoài nhn xét đó. Phng pháp sinh không th sinh ra đc cu hình th
p nu nh cha có cu hình th p - 1, chng t rng phng pháp sinh t ra u đim trong trng
hp lit kê toàn b mt s lng nh cu hình trong mt b d liu ln thì li có nhc đim và
ít tính ph dng trong nhng thut toán duyt hn ch. Hn th na, không phi cu hình ban đu
lúc nào cng d tìm đc, không phi k thut sinh cu hình k tip cho mi bài toán đu đn gin
nh trên (Sinh các chnh hp không lp chp k theo th t t đin chng hn). Ta sang mt chuyên
mc sau nói đn mt phng pháp lit kê có tính ph dng cao hn, đ gii các bài toán lit kê
phc tp hn đó là: Thut toán quay lui (Back tracking).
Bài toán lit kê
Lê Minh Hoàng
{ 12 z
§
3. THUT TOÁN QUAY LUI
Thut toán quay lui dùng đ gii bài toán lit kê các cu hình. Mi cu hình đc xây dng
bng cách xây dng tng phn t, mi phn t đc chn bng cách th tt c các kh nng.
Gi thit cu hình cn lit kê có dng (x
1
, x
2
, , x
n
). Khi đó thut toán quay lui thc hin qua các
bc sau:
1) Xét tt c các giá tr x
1
có th nhn, th cho x

1
nhn ln lt các giá tr đó. Vi mi giá tr th
gán cho x
1
ta s:
2) Xét tt c các giá tr x
2
có th nhn, li th cho x
2
nhn ln lt các giá tr đó. Vi mi giá tr
th gán cho x
2
li xét tip các kh nng chn x
3
c tip tc nh vy đn bc:
n) Xét tt c các giá tr x
n
có th nhn, th cho x
n
nhn ln lt các giá tr đó, thông báo cu hình
tìm đc (x
1
, x
2
, , x
n
).
Trên phng din quy np, có th nói rng thut toán quay lui lit kê các cu hình n phn t dng
(x
1

, x
2
, , x
n
) bng cách th cho x
1
nhn ln lt các giá tr có th. Vi mi giá tr th gán cho x
1
li
lit kê tip cu hình n - 1 phn t (x
2
, x
3
, , x
n
).
Mô hình ca thut toán quay lui có th mô t nh sau:
{Th
 tc này th cho x
i
nh
n ln lt các giá tr mà nó có th nhn}
procedure Try(i: Integer);
begin
for (mi giá tr V có th gán cho x
i
) do
begin
<Th cho x
i

:= V>;
if (x
i
là phn t cui cùng trong cu hình) then
<Thông báo cu hình tìm đc>
else
begin
<Ghi nhn vic cho x
i
nhn giá tr V (Nu cn)>;
Try(i + 1);
{G
i đ quy đ chn tip x
i+1
}
―― <Nu cn, b ghi nhn vic th x
i
:= V, đ th giá tr khác>;
end;
end;
end;
Thut toán quay lui s bt đu bng li gi Try(1)
Ta có th trình bày quá trình tìm kim li gii ca thut toán quay lui bng cây sau:
Try(2)
Try(3) Try(3) Try(3) Try(3)
Try(2)
Try(1)
Hình 1: Cây tìm kim quay lui
Bài toán lit kê
Lê Minh Hoàng

{ 13 z
I. LIT KÊ CÁC DÃY NH PHÂN Đ DÀI N
Input/Output vi khuôn dng nh trong PROG2_1.PAS
Biu din dãy nh phân đ dài N di dng (x
1
, x
2
, , x
n
). Ta s lit kê các dãy này bng cách th
dùng các giá tr {0, 1} gán cho x
i
. Vi mi giá tr th gán cho x
i
li th các giá tr có th gán cho
x
i+1
.Chng trình lit kê bng thut toán quay lui có th vit:
PROG03_1.PAS * Thut toán quay lui lit kê các dãy nh phân đ dài n
program BinaryStrings;
const
max = 30;
var
x: array[1 max] of Integer;
n: Integer;
procedure PrintResult;
{In c
u hình tìm đc, do th tc tìm đ quy
Try g
i khi tìm ra mt cu hình}

var
i: Integer;
begin
for i := 1 to n do Write(x[i]);
WriteLn;
end;
procedure Try(i: Integer);
{Th
 các cách chn x
i
}
var
j: Integer;
begin
for j := 0 to 1 do
{Xét các giá tr
 có th gán cho x
i
, v
i mi giá tr đó}
――――begin
x[i] := j;
{Th
 đt x
i
}
if i = n then PrintResult
{N
u i = n thì in kt qu}
―――― else Try(i + 1);

{N
u i cha phi là phn t cui thì tìm tip x
i+1
}
end;
end;
begin
Assign(Input, 'BSTR.INP'); Reset(Input);
Assign(Output, 'BSTR.OUT'); Rewrite(Output);
ReadLn(n);
{Nh
p d liu}
Try(1);
{Th
 các cách chn giá tr x
1
}
Close(Input);
Close(Output);
end.
Ví d: Khi n = 3, cây tìm kim quay lui nh sau:
Try(3)
Try(2)
Try(3) Try(3) Try(3)
Try(2)
Try(1)
x
1
:= 0
x

1
:= 1
x
2
:= 0
x
2
:= 1
x
2
:= 0
x
2
:= 1
x
3
:= 0
x
3
:= 1
x
3
:= 0
x
3
:= 1
x
3
:= 0
x

3
:= 1
x
3
:= 0
x
3
:= 1
000
001
010
011
100
101
110
111
result
Hình 2: Cây tìm kim quay lui trong bài toán lit kê dãy nh phân
Bài toán lit kê
Lê Minh Hoàng
{ 14 z
II. LIT KÊ CÁC TP CON K PHN T
Input/Output có khuôn dng nh trong PROG02_2.PAS
 lit kê các tp con k phn t ca tp S = {1, 2, , n} ta có th đa v lit kê các cu hình (x
1
, x
2
,
, x
k

)  đây các x
i
∈ S và x
1
< x
2
< < x
k
. Ta có nhn xét:
• x
k
≤ n
• x
k-1
≤ x
k
- 1 ≤ n - 1

• x
i
≤ n - k + i

• x
1
≤ n - k + 1.
T đó suy ra x
i-1
+ 1 ≤ x
i
≤ n - k + i (1 ≤ i ≤ k)  đây ta gi thit có thêm mt s x

0
= 0 khi xét i = 1.
Nh vy ta s xét tt c các cách chn x
1
t 1 (=x
0
+ 1) đn n - k + 1, vi mi giá tr đó, xét tip tt
c các cách chn x
2
t x
1
+ 1 đn n - k + 2, c nh vy khi chn đc đn x
k
thì ta có mt cu
hình cn lit kê. Chng trình lit kê bng thut toán quay lui nh sau:
PROG03_2.PAS * Thut toán quay lui lit kê các tp con k phn t
program Combinations;
const
max = 30;
var
x: array[0 max] of Integer;
n, k: Integer;
procedure PrintResult;
(*In ra t
p con {x
1
, x
2
, , x
k

}*)
var
i: Integer;
begin
Write('{');
for i := 1 to k - 1 do Write(x[i], ', ');
WriteLn(x[k], '}');
end;
procedure Try(i: Integer);
{Th
 các cách chn giá tr cho x[i]}
var
j: Integer;
begin
for j := x[i - 1] + 1 to n - k + i do
begin
x[i] := j;
if i = k then PrintResult
else Try(i + 1);
end;
end;
begin
Assign(Input, 'SUBSET.INP'); Reset(Input);
Assign(Output, 'SUBSET.OUT'); Rewrite(Output);
ReadLn(n, k);
x[0] := 0;
Try(1);
Close(Input); Close(Output);
end.
Bài toán lit kê

Lê Minh Hoàng
{ 15 z
Nu đ ý chng trình trên và chng trình lit kê dãy nh phân đ dài n, ta thy v c bn chúng
ch khác nhau  th tc Try(i) - chn th các giá tr cho x
i
,  chng trình lit kê dãy nh phân ta
th chn các giá tr 0 hoc 1 còn  chng trình lit kê các tp con k phn t ta th chn x
i
là mt
trong các giá tr nguyên t x
i-1
+ 1 đn n - k + i. Qua đó ta có th thy tính ph dng ca thut toán
quay lui: mô hình cài đt có th thích hp cho nhiu bài toán, khác vi phng pháp sinh tun t,
vi mi bài toán li phi có mt thut toán sinh k tip riêng làm cho vic cài đt mi bài mt khác,
bên cnh đó, không phi thut toán sinh k tip nào cng d cài đt.
III. LIT KÊ CÁC CHNH HP KHÔNG LP CHP K
 lit kê các chnh hp không lp chp k ca tp S = {1, 2, , n} ta có th đa v lit kê các cu
hình (x
1
, x
2
, , x
k
)  đây các x
i
∈ S và khác nhau đôi mt.
Nh vy th tc Try(i) - xét tt c các kh nng chn x
i
- s th ht các giá tr t 1 đn n, mà các giá
tr này cha b các phn t đng trc chn. Mun xem các giá tr nào cha đc chn ta s dng

k thut dùng mng đánh du:
• Khi to mt mng c
1
, c
2
, , c
n
mang kiu logic.  đây c
i
cho bit giá tr i có còn t do hay đã
b chn ri. Ban đu khi to tt c các phn t mng c là TRUE có ngha là các phn t t 1
đn n đu t do.
• Ti bc chn các giá tr có th ca x
i
ta ch xét nhng giá tr j có c
j
= TRUE có ngha là ch
chn nhng giá tr t do.
• Trc khi gi đ quy tìm x
i+1
: ta đt giá tr j va gán cho x
i
là đã b chn có ngha là đt c
j
:=
FALSE đ các th tc Try(i + 1), Try(i + 2) gi sau này không chn phi giá tr j đó na
• Sau khi gi đ quy tìm x
i+1
: có ngha là sp ti ta s th gán mt giá tr khác cho x
i

thì ta s đt
giá tr j va th đó thành t do (c
j
:= TRUE), bi khi x
i
đã nhn mt giá tr khác ri thì các phn
t đng sau: x
i+1
, x
i+2
hoàn toàn có th nhn li giá tr j đó. iu này hoàn toàn hp lý trong
phép xây dng chnh hp không lp: x
1
có n cách chn, x
2
có n - 1 cách chn, Lu ý rng khi
th tc Try(i) có i = k thì ta không cn phi đánh du gì c vì tip theo ch có in kt qu ch
không cn phi chn thêm phn t nào na.
Input: file vn bn ARRANGES.INP cha hai s nguyên dng n, k (1 ≤ k ≤ n ≤ 20) cách nhau ít
nht mt du cách
Output: file vn bn ARRANGES.OUT ghi các chnh hp không lp chp k ca tp {1, 2, , n}
ARRANGES.INP ARRANGES.OUT
3 2 1 2
1 3
2 1
2 3
3 1
3 2
PROG03_3.PAS * Thut toán quay lui lit kê các chnh hp không lp chp k
program Arranges;

const
max = 20;
var
x: array[1 max] of Integer;
c: array[1 max] of Boolean;
n, k: Integer;
procedure PrintResult;
{Th
 tc in cu hình tìm đc}
Bài toán lit kê
Lê Minh Hoàng
{ 16 z
var
i: Integer;
begin
for i := 1 to k do Write(x[i],' ');
WriteLn;
end;
procedure Try(i: Integer);
{Th
 các cách chn x
i
}
var
j: Integer;
begin
for j := 1 to n do
if c[j] then
{Ch
 xét nhng giá tr j còn t do}

begin
x[i] := j;
if i = k then PrintResult
{N
u đã chn đc đn xk thì ch vic in kt qu}
―― else
begin
c[j] := False;
{ánh du: j đã b chn}
―――――――― Try(i + 1);
{Th
 tc này ch xét nhng giá tr còn t do gán cho x
i+1
, t
c là s không chn phi j}
―― c[j] := True;
{B
 đánh du: j li là t do, bi sp ti s th mt cách chn khác ca x
i
}
―――――――― end;
end;
end;
begin
Assign(Input, 'ARRANGES.INP'); Reset(Input);
Assign(Output, 'ARRANGES.OUT'); Rewrite(Output);
ReadLn(n, k);
FillChar(c, SizeOf(c), True);
{T
t c các s đu cha b chn}

Try(1);
{Th
 các cách chn giá tr ca x
1
}
Close(Input); Close(Output);
end.
Nhn xét: khi k = n thì đây là chng trình lit kê hoán v
IV. BÀI TOÁN PHÂN TÍCH S
Bài toán
Cho mt s nguyên dng n ≤ 30, hãy tìm tt c các cách phân tích s n thành tng ca các s
nguyên dng, các cách phân tích là hoán v ca nhau ch tính là 1 cách.
Cách làm:
1. Ta s lu nghim trong mng x, ngoài ra có mt mng t. Mng t xây dng nh sau: t
i
s là tng
các phn t trong mng x t x
1
đn x
i
: t
i
:= x
1
+ x
2
+ + x
i
.
2. Khi lit kê các dãy x có tng các phn t đúng bng n, đ tránh s trùng lp ta đa thêm ràng

buc x
i-1
≤ x
i
.
3. Vì s phn t thc s ca mng x là không c đnh nên th tc PrintResult dùng đ in ra 1 cách
phân tích phi có thêm tham s cho bit s in ra bao nhiêu phn t.
4. Th tc đ quy Try(i) s th các giá tr có th nhn ca x
i
(x
i
≥ x
i - 1
)
5. Khi nào thì in kt qu và khi nào thì gi đ quy tìm tip ?
Lu ý rng t
i - 1
là tng ca tt c các phn t t x
1
đn x
i-1
do đó
• Khi t
i
= n tc là (x
i
= n - t
i - 1
) thì in kt qu
• Khi tìm tip, x

i+1
s phi ln hn hoc bng x
i
. Mt khác t
i+1
là tng ca các s t x
1
ti x
i+1
không đc vt quá n. Vy ta có t
i+1
≤ n ⇔ t
i-1
+ x
i
+ x
i+1
≤ n ⇔ x
i
+ x
i + 1
≤ n - t
i - 1
tc là x
i
Bài toán lit kê
Lê Minh Hoàng
{ 17 z
≤ (n - t
i - 1

)/2. Ví d đn gin khi n = 10 thì chn x
1
= 6, 7, 8, 9 là vic làm vô ngha vì nh
vy cng không ra nghim mà cng không chn tip x
2
đc na.
Mt cách d hiu ta gi đ quy tìm tip khi giá tr x
i
đc chn còn cho phép chn thêm mt
phn t khác ln hn hoc bng nó mà không làm tng vt quá n. Còn ta in kt qu ch khi
x
i
mang giá tr đúng bng s thiu ht ca tng i-1 phn t đu so vi n.
6. Vy th tc Try(i) th các giá tr cho x
i
có th mô t nh sau: (đ tng quát cho i = 1, ta đt x
0
=
1 và t
0
= 0).
• Xét các giá tr ca x
i
t x
i - 1
đn (n - t
i-1
) div 2, cp nht t
i
:= t

i - 1
+ x
i
và gi đ quy tìm tip.
• Cui cùng xét giá tr x
i
= n - t
i-1
và in kt qu t x
1
đn x
i
.
Input: file vn bn ANALYSE.INP cha s nguyên dng n ≤ 30
Output: file vn bn ANALYSE.OUT ghi các cách phân tích s n.
ANALYSE.INP ANALYSE.OUT
6 6 = 1+1+1+1+1+1
6 = 1+1+1+1+2
6 = 1+1+1+3
6 = 1+1+2+2
6 = 1+1+4
6 = 1+2+3
6 = 1+5
6 = 2+2+2
6 = 2+4
6 = 3+3
6 = 6
PROG03_4.PAS * Thut toán quay lui lit kê các cách phân tích s
program Analyses;
const

max = 30;
var
n: Integer;
x: array[0 max] of Integer;
t: array[0 max] of Integer;
procedure Init;
{Kh
i to}
begin
ReadLn(n);
x[0] := 1;
t[0] := 0;
end;
procedure PrintResult(k: Integer);
var
i: Integer;
begin
Write(n,' = ');
for i := 1 to k - 1 do Write(x[i], '+');
WriteLn(x[k]);
end;
procedure Try(i: Integer);
var
j: Integer;
begin
for j := x[i - 1] to (n - T[i - 1]) div 2 do
{Tr
ng hp còn chn tip x
i+1
}

begin
x[i] := j;
Bài toán lit kê
Lê Minh Hoàng
{ 18 z
t[i] := t[i - 1] + j;
Try(i + 1);
end;
x[i] := n - T[i - 1];
{N
u x
i
là ph
n t cui thì nó bt buc phi là và in kt qu}
PrintResult(i);
end;
begin
Assign(Input, 'ANALYSE.INP'); Reset(Input);
Assign(Output, 'ANALYSE.OUT'); Rewrite(Output);
Init;
Try(1);
Close(Input);
Close(Output);
end.
Bây gi ta xét tip mt ví d kinh đin ca thut toán quay lui:
V. BÀI TOÁN XP HU
Bài toán
Xét bàn c tng quát kích thc nxn. Mt quân hu trên bàn c có th n đc các quân khác nm
ti các ô cùng hàng, cùng ct hoc cùng đng chéo. Hãy tìm các xp n quân hu trên bàn c sao
cho không quân nào n quân nào.

Ví d mt cách xp vi n = 8:
Hình 3: Xp 8 quân hu trên bàn c 8x8
Phân tích
• Rõ ràng n quân hu s đc đt mi con mt hàng vì hu n đc ngang, ta gi quân hu s đt
 hàng 1 là quân hu 1, quân hu  hàng 2 là quân hu 2 quân hu  hàng n là quân hu n.
Vy mt nghim ca bài toán s đc bit khi ta tìm ra đc v trí ct ca nhng quân hu.
• Nu ta đnh hng ông (Phi), Tây (Trái), Nam (Di), Bc (Trên) thì ta nhn thy rng:
♦ Mt đng chéo theo hng ông Bc - Tây Nam (B-TN) bt k s đi qua mt s ô, các ô
đó có tính cht: Hàng + Ct = C (Const). Vi mi đng chéo B-TN ta có 1 hng s C và
vi mt hng s C: 2 ≤ C ≤ 2n xác đnh duy nht 1 đng chéo B-TN vì vy ta có th đánh
ch s cho các đng chéo B- TN t 2 đn 2n
♦ Mt đng chéo theo hng ông Nam - Tây Bc (N-TB) bt k s đi qua mt s ô, các ô
đó có tính cht: Hàng - Ct = C (Const). Vi mi đng chéo N-TB ta có 1 hng s C và
vi mt hng s C: 1 - n ≤ C ≤ n - 1 xác đnh duy nht 1 đng chéo N-TB vì vy ta có th
đánh ch s cho các đng chéo N- TB t 1 - n đn n - 1.
Bài toán lit kê
Lê Minh Hoàng
{ 19 z
12345678
1
2
3
4
5
6
7
N
S
EW
8

Hình 4: ng chéo B-TN mang ch s 10 và đng chéo N-TB mang ch s 0, ô chung (5, 5)
Cài đt:
1. Ta có 3 mng logic đ đánh du:
• Mng a[1 n]. a
i
= TRUE nu nh ct i còn t do, a
i
= FALSE nu nh ct i đã b mt quân hu
khng ch
• Mng b[2 2n]. b
i
= TRUE nu nh đng chéo B-TN th i còn t do, b
i
= FALSE nu nh
đng chéo đó đã b mt quân hu khng ch.
• Mng c[1 - n n - 1]. c
i
= TRUE nu nh đng chéo N-TB th i còn t do, c
i
= FALSE nu
nh đng chéo đó đã b mt quân hu khng ch.
• Ban đu c 3 mng đánh du đu mang giá tr TRUE. (Các ct và đng chéo đu t do)
2. Thut toán quay lui: Xét tt c các ct, th đt quân hu 1 vào mt ct, vi mi cách đt nh vy,
xét tt c các cách đt quân hu 2 không b quân hu 1 n, li th 1 cách đt và xét tip các cách đt
quân hu 3 Mi cách đt đc đn quân hu n cho ta 1 nghim
3. Khi chn v trí ct j cho quân hu th i, thì ta phi chn ô(i, j) không b các quân hu đt trc đó
n, tc là phi chn ct j còn t do, đng chéo B-TN (i+j) còn t do, đng chéo N-TB(i-j)
còn t do. iu này có th kim tra (a
j
= b

i+j
= c
i-j
= TRUE)
4. Khi th đt đc quân hu th i vào ct j, nu đó là quân hu cui cùng (i = n) thì ta có mt
nghim. Nu không:
• Trc khi gi đ quy tìm cách đt quân hu th i + 1, ta đánh du ct và 2 đng chéo b quân
hu va đt khng ch (a
j
= b
i+j
= c
i-j
:= FALSE) đ các ln gi đ quy tip sau chn cách đt
các quân hu k tip s không chn vào nhng ô nm trên ct j và nhng đng chéo này na.
• Sau khi gi đ quy tìm cách đt quân hu th i + 1, có ngha là sp ti ta li th mt cách đt
khác cho quân hu th i, ta b đánh du ct và 2 đng chéo b quân hu va th đt khng ch
(a
j
= b
i+j
= c
i-j
:= TRUE) tc là ct và 2 đng chéo đó li thành t do, bi khi đã đt quân hu i
sang v trí khác ri thì ct và 2 đng chéo đó hoàn toàn có th gán cho mt quân hu khác
Hãy xem li trong các chng trình lit kê chnh hp không lp và hoán v v k thut đánh du. 
đây ch khác vi lit kê hoán v là: lit kê hoán v ch cn mt mng đánh du xem giá tr có t do
không, còn bài toán xp hu thì cn phi đánh du c 3 thành phn: Ct, đng chéo B-TN,
đng chéo N- TB. Trng hp đn gin hn: Yêu cu lit kê các cách đt n quân xe lên bàn c
nxn sao cho không quân nào n quân nào chính là bài toán lit kê hoán v

Input: file vn bn QUEENS.INP cha s nguyên dng n ≤ 12
Output: file vn bn QUEENS.OUT, mi dòng ghi mt cách đt n quân hu
Bài toán lit kê
Lê Minh Hoàng
{ 20 z
QUEENS.INP QUEENS.OUT
5 (1, 1); (2, 3); (3, 5); (4, 2); (5, 4);
(1, 1); (2, 4); (3, 2); (4, 5); (5, 3);
(1, 2); (2, 4); (3, 1); (4, 3); (5, 5);
(1, 2); (2, 5); (3, 3); (4, 1); (5, 4);
(1, 3); (2, 1); (3, 4); (4, 2); (5, 5);
(1, 3); (2, 5); (3, 2); (4, 4); (5, 1);
(1, 4); (2, 1); (3, 3); (4, 5); (5, 2);
(1, 4); (2, 2); (3, 5); (4, 3); (5, 1);
(1, 5); (2, 2); (3, 4); (4, 1); (5, 3);
(1, 5); (2, 3); (3, 1); (4, 4); (5, 2);
PROG03_5.PAS * Thut toán quay lui gii bài toán xp hu
program n_Queens;
const
max = 12;
var
n: Integer;
x: array[1 max] of Integer;
a: array[1 max] of Boolean;
b: array[2 2 * max] of Boolean;
c: array[1 - max max - 1] of Boolean;
procedure Init;
begin
ReadLn(n);
FillChar(a, SizeOf(a), True);

{M
i ct đu t do}
FillChar(b, SizeOf(b), True);
{M
i đng chéo ông Bc - Tây Nam đu t do}
――FillChar(c, SizeOf(c), True);
{M
i đng chéo ông Nam - Tây Bc đu t do}
end;
procedure PrintResult;
var
i: Integer;
begin
for i := 1 to n do Write('(', i, ', ', x[i], '); ');
WriteLn;
end;
procedure Try(i: Integer);
{Th
 các cách đt quân hu th i vào hàng i}
var
j: Integer;
begin
for j := 1 to n do
if a[j] and b[i + j] and c[i - j] then
{Ch
 xét nhng ct j mà ô (i, j)
ch
a
b
 khng ch}

――――――begin
x[i] := j;
{Th
 đt quân hu i vào ct j}
――――――――if i = n then PrintResult
else
begin
a[j] := False; b[i + j] := False; c[i - j] := False;
{ánh du}
―― Try(i + 1);
{Tìm các cách
đt quân hu th i + 1}
―――――― a[j] := True; b[i + j] := True; c[i - j] := True;
{B
 đánh du}
―――― end;
end;
end;
begin
Assign(Input, 'QUEENS.INP'); Reset(Input);
Assign(Output, 'QUEENS.OUT'); Rewrite(Output);
Init;
Bài toán lit kê
Lê Minh Hoàng
{ 21 z
Try(1);
Close(Input); Close(Output);
end.
Tên gi thut toán quay lui, đng trên phng din cài đt có th nên gi là k thut vét cn bng
quay lui thì chính xác hn, tuy nhiên đng trên phng din bài toán, nu nh ta coi công vic gii

bài toán bng cách xét tt c các kh nng cng là 1 cách gii thì tên gi Thut toán quay lui cng
không có gì trái logic. Xét hot đng ca chng trình trên cây tìm kim quay lui ta thy ti bc
th chn x
i
nó s gi đ quy đ tìm tip x
i+1
có ngha là quá trình s duyt tin sâu xung phía di
đn tn nút lá, sau khi đã duyt ht các nhánh, tin trình lùi li th áp đt mt giá tr khác cho x
i
, đó
chính là ngun gc ca tên gi "thut toán quay lui"
Bài tp:
1. Mt s chng trình trên x lý không tt trong trng hp tm thng (n = 0 hoc k = 0), hãy
khc phc các li đó
2. Vit chng trình lit kê các chnh hp lp chp k ca n phn t
3. Cho hai s nguyên dng l, n. Hãy lit kê các xâu nh phân đ dài n có tính cht, bt k hai xâu
con nào đ dài l lin nhau đu khác nhau.
4. Vi n = 5, k = 3, v cây tìm kim quay lui ca chng trình lit kê t hp chp k ca tp {1, 2, ,
n}
5. Lit kê tt c các tp con ca tp S gm n s nguyên {S
1
, S
2
, , S
n
} nhp vào t bàn phím
6. Tng t nh bài 5 nhng ch lit kê các tp con có max - min ≤ T (T cho trc).
7. Mt dãy (x
1
, x

2
, , x
n
) gi là mt hoán v hoàn toàn ca tp {1, 2, , n} nu nó là mt hoán v và
tho mãn x
i
≠ i vi ∀i: 1 ≤ i ≤ n. Hãy vit chng trình lit kê tt c các hoán v hoàn toàn ca tp
trên (n vào t bàn phím).
8. Sa li th tc in kt qu (PrintResult) trong bài xp hu đ có th v hình bàn c và các cách đt
hu ra màn hình.
9. Bài toán mã đi tun: Cho bàn c tng quát kích thc nxn và mt quân Mã, hãy ch ra mt hành
trình ca quân Mã xut phát t ô đang đng đi qua tt c các ô còn li ca bàn c, mi ô đúng 1 ln.
10. Chuyn tt c các bài tp trong bài trc đang vit bng sinh tun t sang quay lui.
11. Xét s đ giao thông gm n nút giao thông đánh s t 1 ti n và m đon đng ni chúng, mi
đon đng ni 2 nút giao thông. Hãy nhp d liu v mng li giao thông đó, nhp s hiu hai
nút giao thông s và d. Hãy in ra tt c các cách đi t s ti d mà mi cách đi không đc qua nút giao
thông nào quá mt ln.
Bài toán lit kê
Lê Minh Hoàng
{ 22 z
§
4. K THUT NHÁNH CN
I. BÀI TOÁN TI U
Mt trong nhng bài toán đt ra trong thc t là vic tìm ra mt nghim tho mãn mt s điu kin
nào đó, và nghim đó là tt nht theo mt ch tiêu c th, nghiên cu li gii các lp bài toán ti u
thuc v lnh vc quy hoch toán hc. Tuy nhiên cng cn phi nói rng trong nhiu trng hp
chúng ta cha th xây dng mt thut toán nào thc s hu hiu đ gii bài toán, mà cho ti nay
vic tìm nghim ca chúng vn phi da trên mô hình lit kê toàn b các cu hình có th và đánh
giá, tìm ra cu hình tt nht. Vic lit kê cu hình có th cài đt bng các phng pháp lit kê: Sinh
tun t và tìm kim quay lui. Di đây ta s tìm hiu phng pháp lit kê bng thut toán quay lui

đ tìm nghim ca bài toán ti u.
II. S BÙNG N T HP
Mô hình thut toán quay lui là tìm kim trên 1 cây phân cp. Nu gi thit rng ng vi mi nút
tng ng vi mt giá tr đc chn cho x
i
s ng vi ch 2 nút tng ng vi 2 giá tr mà x
i+1

th nhn thì cây n cp s có ti 2
n
nút lá, con s này ln hn rt nhiu ln so vi d liu đu vào n.
Chính vì vy mà nu nh ta có thao tác tha trong vic chn x
i
thì s phi tr giá rt ln v chi phí
thc thi thut toán bi quá trình tìm kim lòng vòng vô ngha trong các bc chn k tip x
i+1
, x
i+2
,
Khi đó, mt vn đ đt ra là trong quá trình lit kê li gii ta cn tn dng nhng thông tin đã tìm
đc đ loi b sm nhng phng án chc chn không phi ti u. K thut đó gi là k thut
đánh giá nhánh cn trong tin trình quay lui.
III. MÔ HÌNH K THUT NHÁNH CN
Da trên mô hình thut toán quay lui, ta xây dng mô hình sau:
procedure Init;
begin
<Khi to mt cu hình bt k BESTCONFIG>;
end;
{Th tc này th chn cho x
i

tt c các giá tr nó có th nhn}
procedure Try(i: Integer);
begin
for (Mi giá tr V có th gán cho x
i
) do
begin
<Th cho x
i
:= V>;
if (Vic th trên vn còn hi vng tìm ra cu hình tt hn BESTCONFIG) then
if (x
i
là phn t cui cùng trong cu hình) then
<Cp nht BESTCONFIG>
else
begin
<Ghi nhn vic th x
i
= V nu cn>;
Try(i + 1);
{G
i đ quy, chn tip x
i+1
}
<B ghi nhn vic th cho x
i
= V (nu cn)>;
end;
end;

end;
begin
Init;
Try(1);
<Thông báo cu hình ti u BESTCONFIG>
end.
Bài toán lit kê
Lê Minh Hoàng
{ 23 z
K thut nhánh cn thêm vào cho thut toán quay lui kh nng đánh giá theo tng bc, nu ti
bc th i, giá tr th gán cho x
i
không có hi vng tìm thy cu hình tt hn cu hình
BESTCONFIG thì th giá tr khác ngay mà không cn phi gi đ quy tìm tip hay ghi nhn kt
qu làm gì. Nghim ca bài toán s đc làm tt dn, bi khi tìm ra mt cu hình mi (tt hn
BESTCONFIG - tt nhiên), ta không in kt qu ngay mà s cp nht BESTCONFIG bng cu hình
mi va tìm đc
IV. BÀI TOÁN NGI DU LCH
Bài toán
Cho n thành ph đánh s t 1 đn n và m tuyn đng giao thông hai chiu gia chúng, mng li
giao thông này đc cho bi bng C cp nxn,  đây C
ij
= C
ji
= Chi phí đi đon đng trc tip t
thành ph i đn thành ph j. Gi thit rng C
ii
= 0 vi ∀i, C
ij
= +∞ nu không có đng trc tip t

thành ph i đn thành ph j.
Mt ngi du lch xut phát t thành ph 1, mun đi thm tt c các thành ph còn li mi thành
ph đúng 1 ln và cui cùng quay li thành ph 1. Hãy ch ra cho ngi đó hành trình vi chi phí ít
nht. Bài toán đó gi là bài toán ngi du lch hay bài toán hành trình ca mt thng gia
(Traveling Salesman)
Cách gii
1) Hành trình cn tìm có dng (x
1
= 1, x
2
, , x
n
, x
n+1
= 1)  đây gia x
i
và x
i+1
: hai thành ph liên
tip trong hành trình phi có đng đi trc tip (C
ij
≠ +∞) và ngoi tr thành ph 1, không thành
ph nào đc lp li hai ln. Có ngha là dãy (x
1
, x
2
, , x
n
) lp thành 1 hoán v ca (1, 2, , n).
2) Duyt quay lui: x

2
có th chn mt trong các thành ph mà x
1
có đng đi ti (trc tip), vi
mi cách th chn x
2
nh vy thì x
3
có th chn mt trong các thành ph mà x
2
có đng đi ti
(ngoài x
1
). Tng quát: x
i
có th chn 1 trong các thành ph cha đi qua mà t x
i-1
có đng đi
trc tip ti.(1 ≤
i ≤ n)
3) Nhánh cn: Khi to cu hình BestConfig có chi phí = +∞. Vi mi bc th chn x
i
xem chi
phí đng đi cho ti lúc đó có < Chi phí ca cu hình BestConfig?, nu không nh hn thì th
giá tr khác ngay bi có đi tip cng ch tn thêm. Khi th đc mt giá tr x
n
ta kim tra xem x
n
có đng đi trc tip v 1 không ? Nu có đánh giá chi phí đi t thành ph 1 đn thành ph x
n

cng vi chi phí t x
n
đi trc tip v 1, nu nh hn chi phí ca đng đi BestConfig thì cp
nht li BestConfig bng cách đi mi.
4) Sau th tc tìm kim quay lui mà chi phí ca BestConfig vn bng +∞ thì có ngha là nó không
tìm thy mt hành trình nào tho mãn điu kin đ bài đ cp nht BestConfig, bài toán không
có li gii, còn nu chi phí ca BestConfig < +∞ thì in ra cu hình BestConfig - đó là hành trình
ít tn kém nht tìm đc
Input: file vn bn TOURISM.INP
• Dòng 1: Cha s thành ph n (1 ≤ n ≤ 20) và s tuyn đng m trong mng li giao thông
• m dòng tip theo, mi dòng ghi s hiu hai thành ph có đng đi trc tip và chi phí đi trên
quãng đng đó (chi phí này là s nguyên dng ≤ 100)
Output: file vn bn TOURISM.OUT
Ghi hành trình tìm đc.

×