Lập Trình Động Nhiều Tác Giả, 96 Trang

background image

1

Lời nói đầu

Lập trình động (còn gọi là phương pháp quy hoạch động) là một kĩ thuật rất

hiệu quả giải quyết nhiều bài toán tin học, đặc biệt là những bài toán tối ưu. Số

lượng bài toán được giải bằng lập trình động cũng rất lớn, ví dụ riêng kì thi

Olympic quốc tế về Tin học IOI 2004 có tới 3 bài trong 6 bài thi có thể giải bằng

lập trình động. Nhiều năm gần đây, trong hầu hết các đề thi chọn HSG QG đều

có ít nhất 1 trong 3 bài có thể giải bằng phương pháp quy hoạch động.

Nhóm tác giả chúng tôi biên tập tài liệu “Bài tập quy hoạch động” này

mong muốn giới thiệu lí thuyết và các bài tập từ đơn giản đến phức tạp của lập

trình động. Cuốn sách sẽ là tài liệu quí báu đối với học sinh năng khiếu Tin học,

sinh viên các ngành công nghệ thông tin và giáo viên môn Tin học của các

trường THPT.

Tài liệu gồm 4 chương:

Chương I: Cơ sở lý thuyết

Chương II: Một số bài tập cơ bản

Chương III: Bài tập chọn lọc

Chương IV: Một số đề tự giải

Chương I nêu rõ tư tưởng, vị trí, ứng dụng của lập trình động và cách nhận

diện các bài tập có thể giải bằng phương pháp quy hoạch động. Chương II phân

tích và dẫn ra chương trình giải các bài toán kinh điển như: Tìm dãy con không

giảm dài nhất, Dãy con chung dài nhất, Tìm dãy con có tổng bằng S, ….Chương

III, chương IV giới thiệu đề bài, cách giải, chương trình của rất nhiều bài tập

chọn lọc.

Chúng tôi chân thành cảm ơn các bạn đồng nghiệp đã nhận xét và góp ý

cho bản thảo, trân trọng cảm ơn BGH trường THPT Chuyên Bắc Giang đã khích

lệ, tạo điều kiện cho nhóm tác giả được nghiên cứu để tài liệu sớm được ra mắt

bạn đọc.

background image

2

Trong quá trình biên soạn, mặc dù chúng tôi đã cố gắng, song nội dung

chuyên đề ngày càng có nhiều khía cạnh mới và sâu sắc nên chắc chắn tài liệu

còn nhiều hạn chế. Chúng tôi rất mong được bạn đọc xa gần góp ý, để lần tái bản

sau tài liệu sẽ hoàn thiện hơn.

Mọi góp ý, xin gửi đến địa chỉ:

Nhóm Tin học Trường THPT Chuyên Bắc Giang

background image

3

Chương I:

Cơ sở lý thuyết

I.1. Tư tưởng của phương pháp quy ho ạch động

I.1.1. Thuật toán chia để trị

Lập trình động cũng như chia để trị là các phương pháp giải một bài toán

bằng cách tổ hợp lời giải các bài toán con của nó.

Thuật toán chia để trị được coi là thuật toán thông dụng nhất trong Tin

học. Khi giải một bài toán P với kích thước ban đầu nào đó nếu gặp trở ngại vì

kích thước quá lớn, người ta thường nghĩ đến việc giải các bài toán tương tự

nhưng với kích thước nhỏ hơn (gọi là các bài toán con của P). Tư tưởng “chia để

trị” thường được nhắc tới như hình ảnh “bẻ dần từng chiếc đũa để bẻ gãy cả

bó đũa”.

Chia để trị thực hiện “tách” một bài toán ban đầu thành các bài toán con

độc lập, sau đó giải các các bài toán con này và tổ hợp dần lời giải từ bài toán

con nhỏ nhất đến bài toán ban đầu.

Nhưng giải từng bài toán con như thế nào? Chúng ta có thể thực hiện một

cách đơn giản lại “tách” bài toán con thành các bài toán con nhỏ hơn nữa, và

cứ tiến hành như vậy cho đến khi gặp các bài toán con nhỏ đến mức dễ dàng

giải được. Các bài toán con cùng được sinh ra sau mỗi lần “tách” được gọi là

cùng mức. Những bài toán con sinh ra sau hơn thì ở mức dưới (thấp hơn).

Thủ tục đệ quy luôn là cách thường dùng và hiệu quả để thực hiện thuật

toán chia để trị. Quá trình đệ quy lần lượt xếp dần các bài toán con vào ngăn

xếp bộ nhớ và sẽ thực hiện giải các bài toán con theo thứ tự ngược lại từ bài

toán đơn giản nhất trên đỉnh ngăn xếp cho đến khi giải được bài toán ban đầu

ở đáy ngăn xếp.

Ví dụ: Cho mảng a[1..n] gồm các số đã sắp tăng. Tìm phần tử của mảng có

giá trị bằng số x cho trước.

background image

4

Xét chương trình thực hiện thuật toán tìm kiếm nhị phân (một kiểu chia

để trị rất phổ biến) để giải bài toán trên. Trong chương trình chính có lời gọi

thủ tục tim(1,n). Sau đây là thủ tục tim(i,j):

Procedure tim(i,j : integer); {Tim x c ó trong mảng a[i .. j] hay không}

Var k : integer;

Begin

K := (i+j) div 2;

If a[k] = x then

Begin

Write(‘Co phan tu bang ‘,x);

Halt;

End

Else

If (a[k] < x) and (k<j) then tim(k+1,j)

Else

If (a[k] > x) and (k>i) then tim(i,k-1)

End;

Do chỉ tìm kiếm trên một trong hai phần được “tách” ra, nên độ phức tạp

thuật toán là O(log

2

n), tốt hơn là tìm kiếm tuần tự.

I.1.2. Hệ thức truy hồi

Giải bài toán thường tiến hành theo nhiều bước. Kết quả của một bước

thường dựa vào kết quả của các bước thực hiện trước đó. Do vậy, cần xây

dựng hệ thức thể hiện quan hệ về kết quả giữa các bước gọi là hệ thức truy

hồi.

Ví dụ 1: Cho dãy số Fibonaci {F

1

, F

2

, …, F

n

}, trong đó:

F

1

= F

2

= 1 và F

n

= F

n-1

+ F

n-2

với n > 2 (1). Tìm số hạng thứ n.

background image

5

Hệ thức tính số hạng thứ n của dãy Fibonaci là hệ thức (1) đã cho sẵn

trong đề bài.

Ví dụ 2: Cho một dãy N số nguyên A

1

, A

2

, …, A

n

. Hãy tìm cách xoá đi một số

ít nhất số hạng để dãy còn lại là đơn điệu hay nói cách khác hãy chọn một số

nhiều nhất các số hạng sao cho dãy B gồm các số hạng đó theo trình tự xuất

hiện trong dãy A là đơn điệu.

Minh hoạ cho ví dụ 2:

Dãy A: 1, 4, 10, 9, 8, 17, 11, 7, 12, 6

Dãy B: 1,4,10,11,12

Phân tích để giải bài toán ta thấy:

Khi dãy A có một phần tử a1thì dãy B có 1 phần tử.

Mở rộng bài toán với dãy A có 2 phần tử, có 3 phần tử, … để tìm độ dài

dãy B. Ta có bài toán tổng quát: Gọi F[i] là số phần tử của dãy con B khi dãy A

có các phần tử từ 1 đến i

Dễ dàng tính được:

F[i] := 1 nếu không tồn tại j:= 1..i-1 để A[j] < A[i]

F[i] := max{F[j], j=1..i-1 thoả mãn a[j] < a[i]} + 1; (2)

Như vậy, để giải bài toán ở ví dụ 2 ta phải tiến hành qua n bước. lời giải ở

bước i (i=1,2,…,n) phụ thuộc vào lời giải của i-1 bước trước đó. Hệ thức (2)

chính là hệ thức truy hồi của bài toán.

I.1.3. Lập trình động là gì?

Lập trình động giống phương pháp chia để trị ở chỗ: lời giải của bài toán

được tổ hợp từ lời giải các bài toán con.

“Chia để trị” sẽ phân chia bài toán ban đầu thành các bài toán con độc lập

(sự phân chia có cấu trúc dạng cây), giải các bài toán con này thường bằng đệ

quy, sau đó tổ hợp lời giải của chúng để được lời giải của bài toán ban đầu.

background image

6

Lập trình động (dynamic programming) cũng phân chia bài toán thành các

bài toán con, nhưng các bài toán con phụ thuộc nhau (hay dùng từ gối nhau

cũng được), mỗi bài toán con có thể tham chiếu tới cùng một số bài toán con

mức dưới (sự phân chia không có cấu trúc dạng cây xem hình 1).

Hình 1: Đồ thị bài toán con cho dãy Fibonacci. Đây không phải là một

cấu

trúc cây

mà là một

đồ thị có hướng phi chu trình

mô tả quan hệ giữa các bài

toán con gối nhau.

Nói rằng một bài toán có các bài toán con trùng nhau có nghĩa là mỗi bài

toán con đó được sử dụng để giải nhiều bài toán lớn hơn khác nhau. Ví dụ,

trong

dãy Fibonacci

, F

3

= F

1

+ F

2

and F

4

= F

2

+ F

3

— khi tính mỗi số đều phải tính

F

2

. Vì tính F

5

cần đến cả F

3

và F

4

, một cách tính F

5

một cách ngây thơ có thể sẽ

phải tính F

2

hai lần hoặc nhiều hơn. Điều này áp dụng mỗi khi có mặt các bài

toán con gối nhau: một cách tiếp cận ngây thơ có thể tốn thời gian tính toán lại

lời giải tối ưu cho các bài toán con mà nó đ ã giải.

Để tránh việc đó, ta lưu trữ lời giải của các bài toán con đã giải. Do vậy,

nếu sau này ta cần giải lại chính bài toán đó, ta có thể lấy và sử dụng kết quả

đã được tính toán. Hướng tiếp cận này được gọi là lưu trữ (trong

tiếng Anh

được gọi là

memoization

, không phải memorization, dù từ này cũng hợp nghĩa).

Nếu ta chắc chắn rằng một lời giải nào đó không còn cần thiết nữa, ta có thể

xóa nó đi để tiết kiệm không gian bộ nhớ. Trong một số trường hợp, ta còn có

thể tính lời giải cho các bài toán con mà ta biết trước rằng sẽ cần đến.

background image

7

Khi giải bài toán Fibonaci cỡ i (với i tăng từ 2 đến n), ta đều sử dụng kết

quả của 2 bài toán con cỡ i-1 và i-2;

Lập trình động do nhà toán học người Mỹ là Richard Bellman (1920-1984)

phát minh vào năm 1953.

Lập trình động thường dùng để giải các bài toán tối ưu – bài toán yêu cầu

tìm một giải pháp tốt nhất trong các giải pháp có thể tìm được.

Cơ sở của lập trình động trong bài toán tối ưu là nguyên lí tối ưu Bellman:

“Dãy tối ưu các quyết định trong một quá trình quyết định nhiều giai đoạn có

thuộc tính là dù trạng thái và các quyết định ban đầu bất kể thế nào, những

quyết định còn lại phải tạo thành một cách giải quyết tối ưu không phụ thuộc

vào trạng thái được sinh ra từ những quyết định ban đầu”

I.1.4. Phương pháp quy hoạch động

Trong ngành khoa học máy tính, phương pháp quy hoạch động là một

phương pháp giảm thời gian chạy của các thuật toán thể hiện các tính chất của

các bài toán con gối nhaucấu trúc con tối ưu.

Tư tưởng chủ đạo của phương pháp quy hoạch động có thể tóm lược như

sau:

Chia 1 bài toán lớn thành các bài toán con tương tự có kích thước nhỏ hơn

cho đến khi nhận được các bài toán con mà lời giải có thể xây dựng dễ dàng.

Ta phải tìm ra mối quan hệ giữa nghiệm của bài toán với nghiệm của các

bài toán con, mối quan hệ thể hiện 1 công thức truy hồi hay một hàm gọi là

công thức quy hoạch động (hay hàm quy hoạch động).

Khi tính toán chúng ta xuất phát tính từ nghiệm các bài toán con ban đầu,

sau đó kết hợp nghiệm của các bài toán con theo công th ức quy hoạch động ta

được nghiệm bài toán con có cỡ lớn hơn. Quá trình cứ tiếp tục như vậy cho

đến khi ta được nghiệm của bài toán đã cho.

background image

8

Vậy: Ý tưởng cơ bản của quy hoạch động thật đơn giản: tránh tính toán

lại mọi thứ hai lần, mà lưu giữ kết quả đã tìm kiếm được vào một bảng làm giả

thiết cho việc tìm kiếm những kết quả của trường hợp sau. Chúng ta sẽ làm

đầy dần giá trị của bảng này bởi các kết quả của những trường hợp trước đã

được giải. Kết quả cuối cùng chính là kết quả của bài toán cần giải

Ví dụ: Tính số hạng thứ n của dãy Fibonaci bằng phương pháp quy hoạch

động như sau:

Nghiệm của mỗi bài toán con chỉ cần tính 1 lần và được lưu trong mảng 1

chiều F. Khi tìm kết quả của các bài toán cỡ lớn hơn, kết quả của các bài toán

con chỉ việc lấy ra để sử dụng.

F1 := 1; F2 := 1;

For i := 3 to n do F[i] := F[i-2] + F[i-1];

I.2. Các bước thực hiện giải bài toán bằng phương pháp quy

hoạch động

I.2.1. Các bước cơ bản:

Bước 1: Phân tích tìm công thức quy hoạch động

- Tìm ra công thức nghiệm của bài toán lớn thông qua việc giải các bài

toán con.

- Xác định nghiệm của bài toán con đơn giản (trong trường hợp cơ sở)

Bước 2: Thực hiện tính toán và tổ chức dữ liệu

- Đầu tiên tính nghiệm của bài toán con đơn giản (cơ sở)

(Khởi tạo giá trị ban đầu cho hàm QHĐ)

- Dựa vào hàm QHĐ đã xây dựng ta tính nghiệm của bài toán theo kích

thước lớn dần cho đến khi nhận được nghiệm của bài toán đã cho.

Bước 3: Tổ chức dữ liệu và cài đặt

background image

9

- Thường dùng mảng một chiều hoặc hai chiều để lưu trữ lại các giá trị

của các bài toán con (giá trị của hàm QHĐ)

- Ngoài ra, chúng ta phải dùng một số biến, thông thường cũng là mảng để

lưu trữ lại trạng thái của bài toán để sau này chúng ta có thể truy xuất lời giải

của bài toán.

I.2.2. Tổ chức cài đặt:

- Có thể dùng vòng lặp

- Hoặc dùng đệ quy

- Nghiệm của các bài toán con được tính toán 1 lần và lưu trữ trong mảng

khi cần lấy ở mảng đó ra và sử dụng

I.2.3 Khi nào dùng phương pháp quy hoạch động?

- Khi gặp bài toán có tính chất truy hồi

- Khi lời giải bài toán ở bước sau được xây dựng thông qua lời giải bài

toán ở bước trước.

- Kích thước bài toán không quá lớn

*Ưu điểm:

- Lời giải ngắn gọn, sang sủa, có tính logic cao, chương trình chạy nhanh.

*Nhược điểm:

- Không phải bài nào cũng giải bằng QHĐ

- Việc tìm ra công thức QHĐ của nhiều bài toán là khó khăn

- Không có công thức chung

- Bị hạn chế về bộ nhớ vì chúng ta phải lưu trữ nghiệm của các bài toán

con.

background image

10

Chương II:

Một số bài tập cơ bản

Bài 1: Tìm dãy con không gi ảm nhiều phần tử nhất;

Cho dãy số nguyên có n phần tử a

1

, a

2

, …, a

n

. Một dãy con không giảm của

dãy đã cho là dãy các phần tử còn lại của dãy đó sau khi ta xóa bỏ một hoặc

một số phần tử bất kì của nó, các phần tử của dãy con tạo thành dãy không

giảm. Ví dụ: dãy 1,4,10,11,12 là một dãy con không giảm của dãy 1, 4, 10, 9, 8,

17, 11, 7, 12, 6.

Yêu cầu: Tìm dãy con không giảm của dãy a gồm nhiều phần tử nhất.

Dữ liệu: Vào từ file văn bản DAYCON.INP gồm:

Dòng đầu tiên ghi số N (0 < N < 10

5

).

Các dòng tiếp theo, mỗi dòng ghi 10 số nguyên của dãy N số nguyên a

1

, a

2

,

.., a

n

. Dòng cuối cùng có thể ít hơn 10 số. Các số trên một dòng cách nhau một

dấu cách.

Kết quả: Ghi ra file văn bản DAYCON.OUT gồm:

Số max là độ dài dãy con không giảm dài nhất tìm được.

Chỉ số xuất hiện của các số hạng của dãy con trong dãy đã cho.

Ví dụ:

DAYCON.INP

DAYCON.OUT

10

6 5 8 12 6 9 7 13 2 13

5

2 5 7 8 10

Hướng dẫn:

Gọi L(i) là độ dài dãy con tăng dài nhất, các phần tử lấy trong miền từ a

1

đến a

i

và phần tử cuối cùng là a

i

.

Ta có công thức QHĐ để tính L(i) như sau:

L(1) = 1

L(i) = max(1, L(j)+1 với mọi phần tử j: 0 < j < i và a

j

≤ a

i

).

background image

11

Bảng phương án là một mảng một chiều L để lưu trữ các giá trị của hàm

QHĐ L(i). Đoạn chương trình tính các giá trị của mảng L như sau:

for i := 1 to n do

begin

L[i] := 1;

for j:=1 to i - 1 do

if (a[j]<=a[i]) and (L[i]<L[j]) then L[i]:=L[j]+1;

end;

Như vậy chi phí không gian của bài toán là O(n), chi phí th ời gian là O(n

2

).

* Chương trình cài đặt:

program day_con_khong_giam_dai_nhat;

uses crt;

const fi='DAYCON.inp';

fo='DAYCON.out';

max=100000;

type arrA=array[0..max]of longint;

arrB=array[1..max,1..max]of longint;

var f:text;

a:arrA;

n,kq,luu,dem:longint;

d,truoc,k:arrA;

procedure init;

var i:longint;

begin

assign(f,fi);

reset(f);

background image

12

readln(f,n);

for i:=1 to n do

read(f,a[i]);

close(f);

end;

procedure process;

var i,j:longint;

begin

for i:=1 to n do

truoc[i]:=i;

d[1]:=1;

for i:=1 to n do

begin

d[i]:=1;

for j:=1 to i-1 do

if a[i]>=a[j] then

if d[j]+1>d[i] then

begin

d[i]:=d[j]+1;

truoc[i]:=j;

end;

end;

kq:=0;

for i:=1 to n do

if d[i]>kq then begin kq:=d[i];luu:=i;end;

dem:=0;

background image

13

while truoc[luu]<>luu do

begin

inc(dem);

k[dem]:=luu;

luu:=truoc[luu];

end;

inc(dem);

k[dem]:=luu;

end;

procedure inkq;

var i:longint;

begin

assign(f,fo);

rewrite(f);

writeln(f,kq);

for i:=dem downto 1 do

write(f,k[i],' ');

close(f);

end;

BEGIN

init;

process;

inkq;

END.

Bài 2: Dãy con chung dài nhất;

Cho hai dãy số nguyên a = (a

1

, a

2

, …, a

M

), b = (b

1

, b

2

, …, b

N

), với M,N < 200.

background image

14

Hãy tìm một dãy con chung c=(c

1

, c

2

, …, c

K

) của a và b gồm nhiều số hạng

nhất. Dãy con chung c nhận được bằng cách xóa đi một số số hạng của dãy a, c

cũng nhận được bằng cách xóa đi một số số hạng của dãy b, sau khi xóa ở hai

dãy giữ nguyên thứ tự các phần tử còn lại.

Dữ liệu: Vào từ file văn bản DAYCC.INP có cấu trúc như sau:

Dòng đầu tiên ghi 2 số nguyên dương M, N.

Dòng thứ 2 ghi các số a

1

, a

2

, …, a

M

;

Dòng thứ 3 ghi các số b

1

, b

2

, …, b

N

;

Kết quả: Ghi ra file văn bản DAYCC.OUT có cấu trúc:

Dòng đầu ghi số K là số lượng các số hạng của dãy con chung c (nếu không

có dãy con chung thì ghi số 0);

Trong k dòng tiếp theo, dòng thứ i (I = 1, 2, .., k) ghi 2 số x, y với ý nghĩa là:

số hạng thứ i của dãy c là số hạng thứ x của dãy a và là số hạng thứ y của dãy

b;

Ví dụ:

DAYCC.INP

DAYCC.OUT

7 6

3 5 1 3 5 5 3

1 5 3 5 3 1

4

2 2

4 3

6 4

7 5

*Hướng dẫn:

Gọi L(i,j) là độ dài dãy con chung dài nhất của dãy a(i) gồm i phần tử

phần đầu của dãy a (a(i) = a[1..i]) và dãy b(j) gồm j phần tử phần đầu của dãy b

background image

15

(b(j)

=b [1..j]).

Ta có công thức quy hoạch động như sau:

L(0,j)=L(i,0)=0.

L(i,j) = L(i - 1,j - 1)+1 nếu a[i] = b[j].

L(i,j) = max(L(i - 1,j), L(i,j - 1)) nếu a[i] ≠ b[j].

*Chương trình cài đặt:

Program Dayconchung;

uses crt;

const fi='daycc.inp';

fo='daycc.out';

maxmn=100;

var f:array[0..maxmn,0..maxmn] of integer;

g:text;

m,n:integer;

a,b:array[1..maxmn] of integer;

Procedure init;

var i:integer;

Begin

assign(g,fi); reset(g);

readln(g,m,n);

for i:=1 to m do read(g,a[i]);

for i:=1 to n do read(g,b[i]);

close(g);

End;

Function max(x,y:integer):integer;

Begin

background image

16

if x > y then max:=x else max:=y;

End;

Procedure Build;

var i,j:integer;

Begin

for i:= 0 to m do f[i,0]:=0;

for i:= 0 to n do f[0,i]:=0;

for i:=1 to m do

for j:=1 to n do

if a[i] <> b[j] then f[i,j] := max(f[i-1,j],f[i,j-1])

else f[i,j] := f[i-1,j-1] + 1;

End;

Procedure result;

var i,j:integer;

Begin

assign(g,fo); rewrite(g);

writeln(g,f[m,n]);

i := m; j := n;

while (i<>0) and (j<>0) do
Begin
if a[i] = b[j] then
Begin
writeln(g,i,' ',j);
dec(i);
dec(j);
End
else

background image

17

Begin
if f[i,j] = f[i-1,j] then dec(i)
else if f[i,j] = f[i,j-1] then dec(j);
end;

end;

close(g);

End;

BEGIN

init;

build;

result;

END.

Bài 3: Dãy con có tổng bằng S

Cho dãy a

1

, a

2

,.. a

n

. Tìm một dãy con của dãy đó có tổng bằng S.

Dữ liệu: vào từ file văn bản "tongs.inp" có dạng :

- Dòng đầu tiên là 2 số N, S (N<200);

- Dòng thứ hai là N số A

i

(i=1, 2,.., N; -32000 <= A

i

<=32000).

Kết quả: Ghi ra file văn bản "tongs.out" có dạng:

- Các phần tử thuộc dãy con tìm được

Ví dụ:

tongs.inp

Tongs.out

8 39

19 5 20 13 16 20 18 2

19 20

* Hướng dẫn:

background image

18

Đặt L(i,t)=1 nếu có thể tạo ra tổng t từ một dãy con của dãy gồm các

phần tử a

1

,a

2

,..a

i

. Ngược lại thì L(i,t)=0. Nếu L(n,S)=1 thì đáp án của bài toán

trên

'có'.

Ta có thể tính L(i,t) theo công thức: L(i,t) = 1 nếu L(i - 1,t)=1 hoặc L(i-1,t -

a[i])=1.

* Chương trình cài đặt:

Program day_con_tong_s;

uses crt;

const fi='tongs.inp';

fo='tongs.out';

max=6400;

maxn=200;

type arrA=array[1..maxn]of longint;

arrB=array[0..max]of longint;

var f:text;

n:longint;

a,truoc:arrA;

s:longint;

d:arrB;

procedure init;

var i:longint;

begin

assign(f,fi);

reset(f);

readln(f,n,s);

for i:=1 to n do

background image

19

read(f,a[i]);

close(f);

end;

procedure process;

var i,j:longint;

begin

for i:=1 to s do d[i]:=0;

for i:=1 to n do

for j:=s downto a[i] do

begin

if (d[j]=0) and (d[j -a[i]]=1) then

begin

d[j]:=1;

truoc[j]:=i;

end;

if d[s]<>0 then exit;

end;

end;

procedure inkq;

var i:longint ;

begin

assign(f,fo);rewrite(f);

if d[s]=0 then write(f,'0')

else

begin

i:=truoc[s];

background image

20

while s<>a[i] do

begin

write(f,a[i],' ');

s:=s-a[i];

i:=truoc[s];

end;

write(f,a[i]);

end;

close(f);

end;

BEGIN

init;

process;

inkq;

END.

Bài 4: Xếp đồ vật vào ba lô (mỗi đồ vật chỉ có 1)

Một chiếc ba lô có thể chứa được một khối lượng không quá W. Có N đồ

vật được đánh số từ 1 đến N, đồ vật i có khối lượng A

i

và giá trị sử dụng C

i

(i=1,

2, …,N). Hãy chọn các đồ vật để xếp vào ba lô sao cho tổng giá trị các đồ vật

trong ba lô là lớn nhất (nếu có thể xếp được).

Dữ liệu: vào từ file văn bản BALO.INP có cấu trúc như sau:

Dòng đầu ghi 2 số nguyên dương N, W (0 < N,W < 100).

Dòng thứ i+1, với i=1,2,..,N, ghi 2 số nguyên dương A

i

và C

i

tương ứng là

khối lượng và giá trị sử dụng của đồ vật thứ I (0 < A

i

, C

i

< 256).

Kết quả: Ghi ra file văn bản BALO.OUT có cấu trúc:

Dòng đầu ghi tổng giá trị sử dụng lớn nhất tìm được.

background image

21

Dòng 2 ghi chỉ số các đồ vật được xếp vào ba lô.

Ví dụ:

BALO.INP

BALO.OUT

7 20

3 9

8 23

23 4

29 34

12 4

1 2

8 6

40

1 2 6 7

*Hướng dẫn

Gọi L(i,t) là tổng giá trị lớn nhất khi được chọn i vật từ 1 đến i cho vào

balô với tổng khối lượng không vượt quá t.

L(n,m) sẽ là đáp số của bài toán (là giá trị lớn nhất có được nếu chọn n vật

và tổng khối lượng không vượt m).

Công thức tính L(i,t) như sau:

L(i,0)=0; L(0,t)=0.

L(i,t)=L(i–1,t) nếu t<A

i

.

L(i,t)=max(L(i–1,t), L(i–1,t–a

i

)+b

i

) nếu t ≥ a

i

. Trong đó: L(i–1,t) là giá trị có

được nếu không đưa vật i vào balô, L(i–1,t–a

i

)+b

i

là giá trị có được nếu chọn vật

i.

Ta có thể dùng một mảng 2 chiều để lưu bảng phương án, tuy nhiên dựa

trên nhận xét rằng để tính dòng i của bảng phương án chỉ cần dòng i–1, ta chỉ

cần dùng 2 mảng một chiều P và L có chỉ số từ 0 đến m để lưu 2 dòng đó. Đoạn

chương trình con tính bảng phương án như sau.

background image

22

L[t] := 0; {với mọi t}

for I := 1 to n do begin

P:=L;

for t := 0 to m do

if t< L[t]:="P[t]" then> else L[t] := max(P[t],P[t –a[i]]);

end;

Nếu để ý kĩ bạn sẽ thấy rằng đoạn trình trên chỉ viết giống công thức

QHĐ chứ chưa tối ưu. Chẳng hạn đã có lệnh gán P:=L, sau đó lại có gán

L[t]:=P[t] với các giá trị t < a[i] là không cần thiết. Bạn đọc có thể tự cải tiến để

chương trình tối ưu hơn. Chi phí không gian của cách cài đặt trên là O(m) và chi

phí thời gian là O(n.m).

*Chương trình cài đặt:

program xep_do_vat;

uses crt;

const fi='balo.inp';

fo='balo.out';

max=10000;

type arrA=array[1..max]of longint;

arrB=array[0..max]of longint;

var f:text;

n,w,kq,luu:longint;

a,c :arrA;

d,gt:arrB;

truoc:arrA;

procedure init;

var i:longint;

background image

23

begin

assign(f,fi);

reset(f);

readln(f,n,w);

for i:=1 to n do

readln(f,a[i],c[i]);

for i:=1 to w do

begin

d[i]:=0;

truoc[i]:=0;

gt[i]:=0;

end;

close(f);

kq:=0;

end;

procedure process;

var i,j:longint;

begin

d[0]:=1; gt[0]:=0;

for i:=1 to n do

begin

for j:=w downto a[i] do

if (d[j-a[i]]=1) then

if gt[j-a[i]]+c[i]>gt[j] then

begin

d[j]:=1;

background image

24

gt[j]:=gt[j-a[i]]+c[i];

truoc[j]:=i;

if gt[j]>kq then

begin

kq:=gt[j];

luu:=j;

end;

end;

write(i,' ',truoc[87]);

readln;

end;

end;

procedure inkq;

var i :longint;

begin

assign(f,fo);

rewrite(f);

writeln(f,kq);

i:=truoc[luu];

while luu>a[i] do

begin

write(f,i,' ');

luu:=luu-a[i];

i:=truoc[luu];

end;

write(f,i);

background image

25

close(f);

end;

BEGIN

init;

process;

inkq;

END.

background image

26

Chương III:

Bài tập chọn lọc

Bài 5: Bố trí phòng họp;

Có n cuộc họp đánh số từ 1 đến n đăng ký làm việc tại một phòng hội

thảo. Cuộc họp i cần được bắt đầu tại thời điểm s

i

và kết thúc tại thời điểm f

i

.

Hỏi có thể bố trí phòng hội thảo phục vụ được nhiều nhất bao nhiêu cuộc họp

sao cho khoảng thời gian làm việc của hai cuộc họp được nhận phục vụ chỉ có

thể giao nhau tại đầu mút.

Dữ liệu: vào từ file văn bản ACTIVITY.INP

Dòng dầu tiên chứa số nguyên dương n (n <= 1000000)

Dòng thứ i trong số n dòng tiếp theo chứa hai số nguyên dương s

i

, f

i

(s

i

, f

i

<= 32000), i=1,2, …,n

Kết quả: Ghi ra file văn bản ACTIVITY.OUT

Dòng đầu tiên ghi số k là số lượng cuộc họp được chấp nhận phục vụ;

Mỗi dòng trong K dòng tiếp theo ghi chỉ số của một trong k cuộc họp được

chấp nhận

Ví dụ:

ACTIVITY.INP

ACTIVITY.OUT

5

1 3

2 4

1 6

3 5

7 9

3

1

4

5

background image

27

* Hướng dẫn:

Sắp xếp các cuộc họp tăng dần theo thời điểm kết thúc (b

i

). Thế thì cuộc

họp i sẽ bố trí được sau cuộc họp j nếu và chỉ nếu j < i va` b

j

≤ a

i

. Yêu cầu bố trí

được nhiều cuộc họp nhất có thể đưa về việc tìm dãy các cuộc họp dài nhất

thoả mãn điều kiện trên

* Chương trình cài đặt:

program bo_tri_phong_hop;

uses crt;

const fi='activity.inp';

fo='activity.out';

max=1000000;

type arrA=array[1..max]of longint;

arrB=array[1..max,1..max]of longint;

var f:text;

n,luu,kq,dem:longint;

a,b,truoc,cs:arrA;

d,k:arrA;

procedure init;

var i:longint;

begin

assign(f,fi);

reset(f);

readln(f,n);

for i:=1 to n do

begin

readln(f,a[i],b[i]);

background image

28

cs[i]:=i;

end;

close(f);

end;

procedure sap_xep;

var i,j,tg:longint;ok:boolean;

begin

for i:=1 to n-1 do

begin

ok:=true;

for j:=n downto i+1 do

if b[j]<b[j-1] then

begin

tg:=b[j];b[j]:=b[j -1];b[j-1]:=tg;

tg:=a[j];a[j]:=a[j-1];a[j-1]:=tg;

tg:=cs[j];cs[j]:=cs[j -1];cs[j-1]:=tg;

ok:=false;

end;

if ok then exit;

end;

end;

procedure process;

var i,j:longint;

begin

d[1]:=1;

for i:=1 to n do

background image

29

truoc[i]:=i;

for i:=2 to n do

begin

d[i]:=1;

for j:=1 to n-1 do

if b[j]<=a[i] then

if d[j]+1>d[i] then

begin

d[i]:=d[j]+1;

truoc[i]:=j;

end;

end;

kq:=-1;

for i:=1 to n do

if d[i]>kq then begin kq:=d[i];luu:=i;end;

while luu<>truoc[luu] do

begin

inc(dem);

k[dem]:=luu;

luu:=truoc[luu];

end;

inc(dem);

k[dem]:=luu;

end;

procedure inkq;

var i:longint;

background image

30

begin

assign(f,fo);

rewrite(f);

writeln(f,kq);

for i:=dem downto 1 do

writeln(f,cs[k[i]]);

close(f);

end;

BEGIN

init;

sap_xep;

process;

inkq;

END.

Bài 6: Cho thuê máy tính;

Tại thời điểm 0, ông chủ cho thuê máy tính nhận được đơn đặt hàng thuê

sử dụng của N khách hàng. Các khách hàng đư ợc đánh số từ 1 đến N. Khách

hàng i cần sử dụng máy từ thời điểm di đến thời điểm ci (di và ci là các số

nguyên và 0 < di < ci < 1000000000), và s ẽ trả tiền sử dụng máy là pi (pi

nguyên, 0 < pi < 10000000);

Yêu cầu: Hãy xác định xem ông chủ cần nhận phục vụ những khách hàng

nào sao cho khoảng thời gian sử dụng máy tính của hai khách hàng được nhận

phục vụ bất kì không giao nhau và tổng tiền thu được từ phục vụ là lớn nhất.

Dữ liệu: vào từ file văn bản RENTING.INP. Dòng đầu tiên ghi số N (0 < N <

1000). Dòng thứ i trong N dòng tiếp theo ghi ba số di, ci, pi cách nhau bởi dấu

trống, i=1,2,..,N.

background image

31

Kết quả: Ghi ra file văn bản RENTING.OUT. Dòng đầu tiên ghi hai số

nguyên dương theo thứ tự là số lượng khách hàng nhận được phục vụ và tổng

tiền thu được. Dòng tiếp theo ghi chỉ số của khách hàng được phục vụ.

Ví dụ:

RENTING.INP

RENTING.OUT

RENTING.INP

RENTING.OUT

3

150 500 150

1 200 100

400 800 80

2 180

2 3

4

400 821 800

200 513 500

100 325 200

600 900 600

2 1100

2 4

*Hướng dẫn:

Tương tự như bài toán 2, nếu sắp xếp các đơn đặt hàng theo thời điểm

kết thúc, ta sẽ đưa được bài toán 3 này về bài toán tìm dãy con có tổng lớn

nhất.

* Chương trình cài đặt:

program cho_thue_may_tinh;

uses crt;

const fi='renting.inp';

fo='renting.out';

max=1000;

type arrA=array[1..max]of int64;

arrB=array[1..max,1..max]of longint;

var f:text;

a,b,c:arrA;

n,kq,luu,dem:longint;

background image

32

d,cs,truoc,k:arrA;

procedure init;

var i:longint;

begin

assign(f,fi);

reset(f);

readln(f,n);

for i:=1 to n do

begin

readln(f,a[i],b[i],c[i]);

cs[i]:=i;

end;

close(f);

end;

procedure sap_xep;

var i,j,tg:longint;ok:boolean;

begin

for i:=1 to n-1 do

begin

ok:=true;

for j:=n downto i+1 do

if b[j]<b[j-1] then

begin

tg:=b[j];b[j]:=b[j -1];b[j-1]:=tg;

tg:=a[j];a[j]:=a[j -1];a[j-1]:=tg;

tg:=cs[j];cs[j]:=cs [j-1];cs[j-1]:=tg;

background image

33

tg:=c[j];c[j]:=c[j -1];c[j-1]:=tg;

ok:=false;

end;

if ok then exit;

end;

end;

procedure

process;

var i,j:longint;

begin

d[1]:=c[1];

for i:=1 to n do

truoc[i]:=i;

for i:=2 to n do

begin

d[i]:=c[i];

for j:=1 to n-1 do

if b[j]<=a[i] then

if d[j]+c[i]>d[i] then

begin

d[i]:=d[j]+c[i];

truoc[i]:=j;

end;

end;

kq:=-1;

for i:=1 to n do

if d[i]>kq then begin kq:=d[i];luu:=i;end;

background image

34

while luu<>truoc[luu] do

begin

inc(dem);

k[dem]:=luu;

luu:=truoc[luu];

end;

inc(dem);

k[dem]:=luu;

end;

procedure inkq;

var i:longint;

begin

assign(f,fo);

rewrite(f);

writeln(f,dem,' ',kq);

for i:=dem downto 1 do

write(f,cs[k[i]],' ');

close(f);

end;

BEGIN

init; sap_xep;

process;

inkq;

END.

Bài 7: Nối điểm

Trên hai đường thẳng song song L1 và L2 ngư ời ta đánh dấu trên mỗi

đường N điểm. Các điểm trên đường thẳng L1 được đánh số từ 1 đến N từ trái

background image

35

sang phải, còn các điểm trên đường thẳng l2 được đánh số p1, p2, …, pN cũng

từ trái qua phải với p1, p2, …, pN là một hoán vị của 1, 2, …, N (Hình vẽ dưới

đây cho một ví dụ khi N=9):

Ta gọi các số gán cho các điểm là số hiệu của chúng. Cho phép nối hai

điểm trên hai đường thẳng có cùng số hiệu.

Yêu cầu tìm cách nối được nhiều cặp điểm nhất với điều kiện các đoạn

nối không được cắt nhau.

Dữ liệu: vào từ file văn bản WIRE.INP có cấu trúc như sau:

Dòng đầu tiên chứa số nguyên dương N (N < 1000);

Dòng thứ hai chứa các số nguyên p1, p2, …, pN cách nhau b ởi dấu trắng;

Kết quả: Ghi ra file văn bản WIRE.OUT có cấu trúc:

Dòng đầu tiên chứa số k là số lượng các đoạn nối tìm được;

Dòng tiếp theo chứa k số hiệu của các đầu mút của các đoạn nối được ghi

theo thứ tự tăng dần.

Ví dụ:

WIRE.INP

WIRE.OUT

9

2 5 3 8 7 4 6 9 1

5

3 4 6 9

*Hướng dẫn:

Đây chính là bài toán 1: Tìm dãy con không gi ảm dài nhất

* Chương trình cài đặt:

program day_con;

uses crt;

background image

36

const fi='wires.inp';

fo='wires.out';

max=1000;

type arrA=array[0..max]of longint;

arrB=array[1..max,1..max]of longint;

var f:text;

a:arrA;

n,kq,luu,dem:longint;

d,truoc,k:arrA;

procedure init;

var i:longint;

begin

assign(f,fi);

reset(f);

readln(f,n);

for i:=1 to n do

read(f,a[i]);

close(f);

end;

procedure process;

var i,j:longint;

begin

for i:=1 to n do

truoc[i]:=i;

d[1]:=1;

for i:=1 to n do

background image

37

begin

d[i]:=1;

for j:=1 to i-1 do

if a[i]>=a[j] then

if d[j]+1>d[i] then

begin

d[i]:=d[j]+1;

truoc[i]:=j;

end;

end;

kq:=0;

for i:=1 to n do

if d[i]>kq then begin kq:=d[i];luu:=i;end;

dem:=0;

while truoc[luu]<>luu do

begin

inc(dem);

k[dem]:=luu;

luu:=truoc[luu];

end;

inc(dem);

k[dem]:=luu;

end;

procedure inkq;

var i:longint;

begin

background image

38

assign(f,fo);

rewrite(f);

writeln(f,kq);

for i:=dem downto 1 do

write(f,a[k[i]],' ');

close(f);

end;

BEGIN

init;

process; inkq;

END.

Bài 8: Dãy con đổi chiều, đối dấu dài nhất;

Cho dãy a

1

, a

2

,... a

n

. Hãy tìm dãy con đổi chiều dài nhất của dãy đó. Dãy

con con đổi dấu a

i1

,a

i2

,... a

ik

phải thoả mãn các điều kiện sau:

a

i1

< a

i2

> a

i3

<... hoặc a

i1

> a

i2

< a

i3

>... các chỉ số phải cách nhau ít nhất L: i

2

- i

1

≥ L, i

3

-i

2

≥ L... chênh lệch giữa 2 phần tử liên tiếp nhỏ hơn U: |a

i1

- a

i2

| ≤ U,

|a

i2

- a

i3

| ≤ U...

Ví dụ: Cho dãy 10 phần tử: -1, 3, -4, 13, 6, 9, -2, 12, -3, 15 và L=2 và U=3

Có 1 dãy con đổi chiều dài nhất là: -1, -4, -2, -3

Dữ liệu: Vào từ file văn bản DOICHIEU.INP có cấu trúc như sau:

Dòng đầu tiên ghi 3 số nguyên dương n, L, U (n <10

5

; 1<L<n-1; 0 < U <

1000);

Các dòng tiếp theo ghi n số của dãy a; (|a

i

| <= 32000);

Kết quả: Ghi ra file văn bản DOICHIEU.OUT có cấu trúc như sau:

Dòng đầu là độ dài dãy con đổi chiều dài nhất tìm được;

Chỉ số các phần tử của dãy con tìm được trong dãy ban đầu;

background image

39

Ví dụ:

DOICHIEU.INP

DOICHIEU.OUT

10 2 3

-1 3 -4 13 6 9 -2 12 -3 15

4

1 3 7 9

* Hướng dẫn:

Gọi L(i) là số phần tử của dãy con đổi dấu có phần tử cuối cùng là a

i

phần tử cuối cùng lớn hơn phần tử đứng trước. Tương tự, P(i) là số phần tử

của dãy con đổi dấu có phần tử cuối cùng là a

i

và phần tử cuối cùng nhỏ hơn

phần tử đứng trước. Ta dễ dàng suy ra:

L(i) = max(1, P(j)+1): j ≤ i - L và a

i

- U ≤ a

j

< a

i

.

P(i) = max(1, L(j)+1): j ≤ i - L và a

i

< a

j

≤ a

i

+ U.

* Chương trình cài đặt:

program day_con_doi_chieu_dai_nhat;

uses crt;

const fi='doichieu.inp';

fo='doichieu.out';

max=100000;

type arrA=array[1..max]of longint;

arrB=array[1..max,1..max]of longint;

var f:text;

n,l,u,luu,kq,cs,dem:longint;

a:arrA;

d1,d2,truoc1,truoc2,k:arrA;

procedure init;

var i:longint;

begin

background image

40

assign(f,fi);

reset(f);

readln(f,n,l,u);

for i:=1 to n do

begin

read(f,a[i]);

truoc1[i]:=i;

truoc2[i]:=i;

end;

close(f);

end;

procedure process;

var i,j:longint;ok:boolean;

begin

d1[1]:=1;

d2[1]:=1;

for i:=2 to n do

begin

d1[i]:=1;

for j:=1 to i-l do

if a[i]>=a[j] then

if abs(a[i]-a[j])<=U then

if d2[j]+1>d1[i] then

begin

d1[i]:=d2[j]+1;

truoc1[i]:=j;

background image

41

end;

d2[i]:=1;

for j:=1 to i-l do

if a[i]<=a[j] then

if abs(a[i]-a[j])<=U then

if d1[j]+1>d2[i] then

begin

d2[i]:=d1[j]+1;

truoc2[i]:=j;

end;

end;

kq:=-1;

for i:=1 to n do

begin

if d1[i]>kq then begin kq:=d1[i];cs:=1;luu:=i;end;

if d2[i]>kq then begin kq:=d2[i];cs:=2;luu:=i;end;

end;

end;

procedure

truy_xuat;

var ok:boolean;

begin

ok:=false;

dem:=0;

repeat

inc(dem);

k[dem]:=luu;

background image

42

if cs=1 then

begin

if luu=truoc1[luu]then

exit

else

begin

luu:=truoc1[luu];

cs:=2;

end;

end

else

begin

if luu=truoc2[luu] then

exit

else

begin

luu:=truoc2[luu];

cs:=1;

end;

end;

until ok;

end;

procedure inkq;

var i:longint;

begin

assign(f,fo);

background image

43

rewrite(f);

writeln(f,kq);

for i:=dem downto 1 do

write(f,k[i],' ');

close(f);

end;

BEGIN

init; process;

truy_xuat; inkq;

END.

Bài 9: Số phép biến đổi ít nhất

Cho 2 xâu X,Y. Có 3 phép biến đổi với xâu X: chèn 1 kí tự, thay thế một kí

tự hoặc xoá một kí tự. Hãy tìm số ít nhất các phép biến đổi để biến xâu X

thành

xâu

Y.

Ví dụ: Cho xâu X=’kitten’ và Y=’sitting’ thì c ần ít nhất là 3 phép biến đổi

Dữ liệu: Vào từ file văn bản BIENDOI.INP có cấu trúc như sau:

Dòng 1 chứa xâu X

Dòng 2 chứa xâu Y

(Mỗi xâu có không quá 200 kí tự)

Kết quả: Ghi ra file văn bản BIENDOI.OUT ghi 1 số là số phép biến đổi ít

nhất tìm được;

Ví dụ:

BIENDOI.INP

BIENDOI.OUT

kitten

sitting

3

* Hướng dẫn:

background image

44

Gọi F(i,j) là số phép biến đổi ít nhất để biến xâu X(i) gồm i kí tự phần đầu

của X (X(i)= X[1..i]) thành xâu Y(j) g ồm j kí tự phần đầu của Y (Y(j) =Y[1..j]). Dễ

thấy F(0,j)=j và F(i,0)=i.

Nếu X[i]=Y[j] thì ta chỉ phải biến đổi xâu X(i-1) thành xâu Y(j-1). Do đó

F(i,j)=F(i-1,j-1).

Ngược lại, ta có 3 cách biến đổi:

- Xoá kí tự X[i] và biến đổi xâu X(i-1) thành Y(j). Khi đó F(i,j)=F(i-1,j)+1.

- Thay thế X[i] bởi Y[j] và biến đổi X(i-1) thành Y(j-1). Khi đó F(i,j)=F(i-

1,j-1)+1.

- Chèn Y[j] vào X(i) và biến đổi X(i) thành Y(j-1). Khi đó F(i,j)=F(i,j-1)+1.

Tổng kết lại, ta có công thức QHĐ:

F(0,j)=j

F(i,0)=i

F(i,j) =F(i - 1,j - 1) nếu X[i] = Y[j].

F(i,j) = min(F(i - 1,j),F(i,j - 1),F(i - 1,j - 1))+1 nếu X[i] ≠ Y[j].

*Chương trình cài đặt:

program bien_doi_xau;

uses crt;

const fi='biendoi.inp';

fo='biendoi.out';

max=100;

type arrA=array[1..max]of char;

arrB=array[0..max,0..max]of longint;

var f:text;

m,n:longint;

d:arrB;

background image

45

a,b:arrA;

procedure init;

begin

assign(f,fi);

reset(f);

m:=0;

while not eoln(f) do

begin

inc(m);read(f,a[m]);

end;

readln(f);

n:=0;

while not eof(f) do

begin

inc(n);read(f,b[n]);

end;

close(f);

end;

function min3so(x,y,z:longint):longint;

var tam:longint;

begin

if x<y then tam:=x

else tam:=y;

if z<tam then tam:=z;

min3so:=tam;

end;

background image

46

procedure

process;

var i,j:longint;

begin

for i:=1 to m do d[i,0]:=i;

for j:=1 to n do d[0,j]:=j;

for i:=1 to m do

for j:=1 to n do

begin

if a[i]=b[j] then d[i,j]:=d[i -1,j-1]

else

d[i,j]:=min3so(d[i-1,j-1],d[i,j-1],d[i-1,j])+1;

end;

end;

procedure inkq;

begin

assign(f,fo);

rewrite(f);

writeln(f,d[m,n]);

close(f);

end;

BEGIN

init;

process;

inkq;

END.

background image

47

Bài 10: Xâu đối xứng;

Một xâu gọi là xâu đối xứng (palindrom) nếu xâu đó đọc từ trái sang phải

hay từ phải sang trái đều như nhau. Cho một xâu S, hãy tìm số kí tự ít nhất cần

thêm vào S để S trở thành xâu đối xứng.

Ví dụ: Cho xâu S=’abcda’ thì số kí tự ít nhất cần chèn thêm là 2

Dữ liệu: Vào từ file văn bản XDX.INP là 1 xâu S có không quá 500 kí t ự)

Kết quả: Ghi ra file văn bản XDX.OUT là số lượng kí tự ít nhất cần thêm;

Ví dụ:

XDX.INP

XDX.OUT

abcda

2

*Hướng dẫn:

Gọi L(i,j) là số kí tự ít nhất cần thêm vào xâu con S[i..j] của S để xâu đó trở

thành đối xứng. Đáp số của bài toán sẽ là L(1,n) với n là số kí tự của S. Ta có

công thức sau để tính L(i,j):

L(i,i)=0.

L(i,j)=L(i+1,j - 1) nếu S[i]=S[j]

L(i,j)=max(L(i+1,j), L(i,j - 1)) nếu S[i] ≠ S[j] ;

Ta có thuật toán đơn giản hơn như sau:

Gọi P là xâu đảo của S và T là xâu con chung dài nh ất của S và P. Khi đó

các kí tự của S không thuộc T cũng là các kí tự cần thêm vào để S trở thành đối

xứng. Đáp số của bài toán sẽ là n - k, với k là độ dài của T. Ví dụ:

S=<b>edbabcd, xâu đảo của S là P=<b>dcbabde. Xâu con chung dài nh ất của S

và P là T=<b>dbabd. Như v ậy cần thêm 2 kí tự là ec vào để S trở thành xâu

đối xứng;

*Chương trình cài đặt:

program xau_doi_xung;

background image

48

uses crt;

const fi='palin.inp';

fo='palin.out';

max=5000;

type arrA=array[1..max]of char;

arrB=array[1..max,1..max]of longint;

var a:arrA;

l:arrB;

f:text;

n:integer;

procedure init;

var i:integer;

begin

assign(f,fi);

reset(f);

readln(f,n);

for i:=1 to n do read(f,a[i]);

close(f);

end;

function min2so(x,y:longint):longint;

begin

if x>y then min2so:=y

else min2so:=x;

end;

procedure process;

var i,j:longint;

background image

49

begin

for i:=1 to n do

l[i,i]:=0;

for i:=1 to n-1 do

for j:=1 to n-i do

begin

if a[j]=a[j+i]then

begin

if i=1 then l[j,j+i]:=0

else

l[j,j+i]:=l[j+1,j+i -1];

end

else

l[j,j+i]:=min2so(l[j+1,j+i],l[j,j+i-1])+1;

end;

end;

procedure inkq;

begin

assign(f,fo);

rewrite(f);

writeln(f,l[1,n]);

close(f);

end;

BEGIN

init;

process;

background image

50

inkq;

END.

Bài 11: Bài toán chia kẹo

Có N gói kẹo, gói thứ i có A

i

cái kẹo. Không được bóc bất kỳ một gói kẹo

nào, cần chia N gói kẹo thành hai phần sao cho độ chênh lệch số kẹo giữa hai

gói là ít nhất.

Dữ liệu:Vào từ file văn bản "chiakeo.inp" có dạng :

- Dòng đầu tiên là số N (N<=100);

- Dòng thứ hai là N số A

i

(i=1, 2,.., N; A

i

<=100).

Kết quả: Ghi ra file văn bản "chiakeo.out" có dạng:

- Dòng đầu là độ chênh lệch nhỏ nhất giữa hai phần có thể được.

- Dòng thứ hai là các chỉ số của của các phần tử trong dãy a thuộc phần

1 ;

- Dòng thứ ba là các chỉ số của các phần tử trong dãy a thuộc phần 2 ;

Ví dụ:

CHIAKEO.INP

CHIAKEO.OUT

8
19 5 20 13 16 20 18 2

1 56 57
3 5 6
1 2 4 7 8

*Hướng dẫn:

Gọi T là tổng số kẹo của n gói. Chúng ta cần tìm số S lớn nhất thoả mãn:

S ≤ T/2.

Có một dãy con của dãy a có tổng bằng S. Khi đó sẽ có cách chia với

chênh lệch 2 phần là T- 2S là nhỏ nhất và dãy con có tổng bằng S ở trên gồm

các phần tử là các gói kẹo thuộc phần thứ nhất. Phần thứ hai là các gói kẹo

còn lại.

* Chương trình cài đặt:

program chia_keo;

background image

51

uses crt;

const fi='chiakeo.inp';

fo='chiakeo.out';

max=100;

var a:array[1..max]of integer;

n:integer;

t,k:integer;

s:array[0..max,0..10000]of integer;

f:text;

t1,lech:integer;

cx:array[1..max]of boolean;

procedure init;

var i:integer;

begin

assign(f,fi);

reset(f);

readln(f,n);

for i:=1 to n do

read(f,a[i]);

close(f);

k:=0;

for i:=1 to n dok:=k+a[i];

t:=k div 2;

for i:=1 to n do s[i,0]:=1;

for i:=1 to t do s[0,i]:=0;

for i:=1 to n do cx[i]:=true;

background image

52

end;

procedure process;

var i,j:integer;

begin

for i:=1 to n do

for j:=1 to t do

begin

s[i,j]:=0;

if s[i-1,j]=1 then s[i,j]:=1;

if j>=a[i] then

if s[i-1,j-a[i]]=1 then s[i,j]:=1;

end;

end;

procedure xu_li;

var i,j:integer;

begin

for i:=t downto 1 do

begin

for j:=1 to n do

if s[j,i]=1 then

begin

t1:=i;

lech:=k-2*i;

exit;

end;

end;

background image

53

end;

procedure truy_ket_qua;

var tam,i,t2:integer;

begin

tam:=t1;

t2:=n;

repeat

for i:=1 to t2 do

if s[i,tam]=1 then

begin

t2:=i;

tam:=tam-a[i];

cx[i]:=false;

break;

end;

until (tam=0);

end;

procedure inkq;

var i:integer;

begin

assign(f,fo);

rewrite(f);

writeln(f,lech,' ',t1,' ',k-t1);

for i:=1 to n do if cx[i]=false then write(f,i,' ');

writeln(f);

for i:=1 to n do if cx[i]=true then write(f,i,' ');

background image

54

close(f);

end;

BEGIN

init;

process;

xu_li;

truy_ket_qua;

inkq;

END.

Bài 12: Mua cá theo lýợng (Olympic Balkan 2000) ;

Người đánh cá Clement bắt được n con cá, khối lượng mỗi con là a

i

, đem

bán ngoài chợ. Ở chợ cá, người ta không mua cá theo t ừng con mà mua theo

một lượng nào đó và một con cá bất kì không được cắt ra. Chẳng hạn 3 kg,

5kg...

Ví dụ: có 3 con cá, khối lượng lần lượt là: 3, 2, 4. Mua lượng 6 kg sẽ phải

lấy con cá thứ 2 và và thứ 3. Mua lượng 3 kg thì lấy con thứ nhất. Không thể

mua lượng 8 kg.

Nếu bạn là người đầu tiên mua cá, có bao nhiêu lượng bạn có thể chọn?

Dữ liệu vào: Đọc từ file văn bản MARKET.INP có cấu trúc:

-

Dòng đầu tiên ghi số nguyên n (n <= 10

4

) ;

-

Các dòng tiếp theo ghi các giá trị a

i

(i=1,2,..,n)

Kết quả ra: Ghi ra file văn bản MARKET.OUT ghi số l là số lượng tìm được.

Ví dụ:

MARKET.INP

MARKET.OUT

3
3 2 4

7

background image

55

* Hướng dẫn:

Thực chất bài toán là tìm các số S mà có một dãy con của dãy a có tổng

bằng S. Ta có thể dùng phương pháp đánh dấu của bài chia kẹo ở trên rồi đếm

các giá trị t mà L[t]=1.

* Chương trình cài đặt:

program so_ca;

uses crt;

const fi='market.inp';

fo='market.out';

maxn=10000;

max=1000000;

type arrA=array[1..maxn]of longint;

arrB=array[0..max]of longint;

var f:text;

a:arrA;

s,dem,n:longint;

d:arrB;

procedure init;

var i:longint;

begin

assign(f,fi);

reset(f);

readln(f,n);

s:=0;

for i:=1 to n do

begin

background image

56

read(f,a[i]);

s:=s+a[i];

end;

close(f);

end;

procedure process;

var i,j:longint;

begin

for i:=1 to s do

d[i]:=0;

d[0]:=1;

for i:=1 to n do

for j:=s downto a[i] do

begin

if (d[j]=0) and (d[j-a[i]]=1) then

begin

d[j]:=1;

inc(dem);

end;

end;

end;

procedure inkq;

begin

assign(f,fo);

rewrite(f);

writeln(f,dem);

background image

57

close(f);

end;

Bài 13: Điền dấu hỏi vào biểu thức

Cho n số tự nhiên a

1

,a

2

,...,a

n

. Ban đầu các số được đặt liên tiếp theo đúng

thứ tự cách nhau bởi dấu '?': a

1

?a

2

?...?a

n

. Cho trước số nguyên S, có cách nào

thay các dấu '?' bằng dấu + hay dấu - để được một biểu thức số học cho giá trị

là S không?

Dữ liệu vào: Đọc từ file văn bản DAUHOI.INP có cấu trúc như sau:

Dòng đầu ghi 2 số nguyên dương n và S (n <=10

4

, S<=10

5

);

Các dòng tiếp theo ghi n giá trị a

1

, a

2

, …, a

n

Kết quả ra: Ghi ra file văn bản DAUHOI.OUT có cấu trúc như sau:

Dòng đầu ghi 0 hoặc 1 tương ứng là không có cách thay các d ấu hỏi hoặc

có tồn tại cách thay dấu hỏi bằng dấu + hay - để giá trị của biểu thức bằng S;

Nếu dòng 1 ghi số 1 thì dòng tiếp theo ghi 1 cách thay dấu hỏi bởi phép

toán + hoặc - tìm được (chỉ cần ghi các dấu theo thứ tự từ trái sang phải)

Ví dụ:

DAUHOI.INP DAUHOI.OUT

5 20

4 5 6 7 8

1

-+++

*Hướng dẫn:

Đặt L(i,t)=1 nếu có thể điền dấu vào i số đầu tiên và cho kết quả bằng t.

Ta có công thức sau để tính L:

L(1,a[1]) =1.

L(i,t)=1 nếu L(i - 1,t+a[i])=1 hoặc L(i - 1,t - a[i])=1.

Nếu L(n,S)=1 thì câu trả lời của bài toán là có. Khi cài đặt, có thể dùng

một mảng 2 chiều (lưu toàn bộ bảng phương án) hoặc 2 mảng một chiều (để

background image

58

lưu dòng i và dòng i - 1). Chú ý là chỉ số theo t của các mảng phải có cả phần âm

(tức là từ - T đến T, với T là tổng của n số), vì trong bài này chúng ta dùng c ả

dấu - nên có thể tạo ra các tổng âm.

Bài này có một biến thể là đặt dấu sao cho kết quả là một số chia hết cho

k. Ta có thuật giải tương tự bài toán trên bằng cách thay các phép cộng, trừ

bằng các hép cộng và trừ theo môđun k và dùng mảng đánh dấu với các giá trị

từ 0 đến k - 1 (là các số dư có thể có khi chia cho k). Đáp số của bài toán là

L(n,0).

* Chương trình cài đặt:

program dau_hoi;

uses crt;

const fi='daudoi.inp';

fo='dauhoi.out';

max=10000;

maxs=100000;

type arrA=array[1..max]of longint;

arrB=array[0..maxs]of longint;

var f:text;

n,s,tong:longint;

a:arrA;

d:arrB;

ok:boolean;

truoc:arrA;

co:array[1..max]of boolean;

procedure init;

background image

59

var

i:longint;

begin

assign(f,fi);

reset(f);

readln(f,n,s);

tong:=0;

for i:=1 to s do

begin

read(f,a[i]);

tong:=tong+a[i];

co[i]:=true;

end;

ok:=true;

if tong<s then ok:=false;

tong:=tong-s;

if tong mod 2 <>0 then ok:=false

else tong:=tong div 2;

close(f);

end;

procedure process;

var i,j:longint;

begin

for i:=1 to tong do

d[i]:=0;

d[0]:=1;

background image

60

for i:=1 to n do

for j:=tong downto a[i] do

begin

if (d[j]=0) and (d[j-a[i]]=1) then

begin

d[j]:=1;

truoc[j]:=i;

end;

if d[tong]<>0 then exit;

end;

end;

procedure inkq;

var luu,i:longint;

begin

assign(f,fo);

rewrite(f);

if (d[tong]=0) or (truoc[tong]=1) then

ok:=false

else

begin

s:=tong;luu:=truoc[s];

while s<>a[luu] do

begin

co[luu]:=false;

s:=s-a[luu];

luu:=truoc[s];

background image

61

end;

co[luu]:=false;

end;

if ok then

begin

writeln(f,'1');

for i:=2 to n do

begin

if co[i] then write(f,'+')

else write(f,'-');

end;

end

else write(f,'0');

close(f);

end;

BEGIN init;

if ok then process; inkq;

END.

Bài 14: Chia thành hai nhóm có tích lớn nhất (ACM 10690)

Cho n số nguyên. Hãy chia chúng thành 2 nhóm sao cho tích c ủa tổng 2

nhóm là lớn nhất.

Dữ liệu vào: Đọc từ file văn bản EXPRESSION.INP có cấu trúc như sau:

-

Dòng đầu ghi 1 số nguyên dương n (n <=10

4

);

-

Các dòng tiếp theo ghi n giá trị a

1

, a

2

, …, a

n

Kết quả ra: Ghi ra file văn bản EXPRESSION.OUT có cấu trúc như sau:

-

Giá trị S là tích lớn nhất của tổng 2 nhóm t ìm được;

background image

62

Ví dụ:

EXPRESSION.INP

EXPRESSION.OUT

5

1 2 3 4 5

56

*Hướng dẫn:

Gọi T là tổng n số nguyên đó. Giả sử ta chia dãy thành 2 nhóm, gọi S là

tổng của một nhóm, tổng nhóm còn lại là T - S và tích của tổng 2 nhóm là S*(T -

S). Bằng phương pháp đánh dấu ta xác định được mọi số S là tổng của một

nhóm (như bài Market) và t ìm số S sao cho S*(T - S) đạt max.

*Chương trình cài đặt:

uses crt;

const fi='expression.inp';

fo='expression.out';

max=10000;

maxs=100000;

type arrA=array[1..max]of longint;

arrB=array[0..maxs]of longint;

var f:text;

n,s,tong:longint;

a:arrA;

d:arrB;

ok:boolean;

procedure init;

var i:longint;

background image

63

begin

assign(f,fi);

reset(f);

readln(f,n);

for i:=1 to n do

begin

read(f,a[i]);

tong:=tong+a[i];

end;

close(f);

end;

procedure process;

var i,j:longint;

begin

for i:=1 to s do

d[i]:=0;

d[0]:=1;

for i:=1 to n do

for j:=s downto a[i] do

begin

if (d[j]=0) and (d[j-a[i]]=1) then

d[j]:=1;

if d[s]<>0 then begin ok:=true;exit;end;

end;

end;

procedure xu_li;

background image

64

begin

s:=tong div 2 ;

while not ok do process;

end;

procedure inkq;

begin

assign(f,fo);

rewrite(f);

if n<>1 then

write(f,(s+1)*(tong-s-1))

else writeln(f,'0');

close(f);

end;

BEGIN

init;

if n<>1 then xu_li;

inkq;

END.

Bài 15: Bài toán mua bán hàng

Có một người đi mua hàng, anh ta có N đồng tiền d

1

, d

2

,.., d

N

. Người bán

hàng có M đồng tiền b

1

, b

2

,.., b

m

. Anh ta muốn mua một mặt hàng với giá trị W.

Hỏi cuộc mua bán có thể diễn ra được không?

Giới hạn: M, N<=100 và d

i

, b

j

<=100.

Dữ liệu vào đọc từ file văn bản MUABAN.INP có cấu trúc như sau:

-

Dòng đầu ghi 3 số nguyên dương N, M, W

-

Dòng thứ 2 ghi N số d

1

, d

2

,.., d

N

;

background image

65

-

Dòng thứ 3 ghi M số b

1

, b

2

,.., b

m

;

Kết quả ghi ra file văn bản MUABAN.OUT có cấu trúc như sau:

-

Dòng đầu ghi số 0 hoặc 1 tương ứng là không thể diễn ra cuộc mua

bán hoặc có thể diễn ra cuộc mua bán;

-

Nếu dòng đầu là 1 thì dòng 2 ghi chỉ số các đồng tiền mà người mua

hàng đã sử dụng để trao đổi, còn dòng 3 ghi chỉ số các đồng tiền mà

người bán hàng đã sử dụng để trao đổi;

Ví dụ:

MUABAN.INP

MUABAN.OUT

MUABAN.INP

MUABAN.OUT

6 10 30

5 7 6 11 2 26

1 2 3 5 6 7

8 9 10

1

1 2 3 4 5

1

4 3 30

6 11 2 26

10 20 30

0

* Chương trình cài đặt:

program mua_ban_hang;

uses crt;

const fi='muaban.inp';

fo='muaban.out';

max=100;

maxs=10000000;

type arrA=array[1..max]of longint;

arrB=array[0..maxs]of longint;

var f:text;

a,b:arrA;

d:arrB;

n,m,w,tong,luu1,luu2,s,v:longint;

ok1,ok2,ok:boolean;

background image

66

truoc1,truoc2:arrA;

procedure init;

var i:longint;

begin

assign(f,fi);

reset(f);

readln(f,n,m,w);

tong:=0;

for i:=1 to n do

begin

read(f,a[i]);

tong:=tong+a[i];

end;

readln(f);

for i:=1 to m do

read(f,b[i]);

close(f);

end;

procedure process1;

var i,j:longint;

begin

for i:=1 to s do

begin

d[i]:=0;

truoc1[i]:=0;

end;

background image

67

d[0]:=1;

for i:=1 to n do

for j:=s downto a[i] do

begin

if (d[j]=0) and (d[j-a[i]]=1) then

begin

d[j]:=1;

truoc1[j]:=i;

end;

if d[s]<>0 then begin ok1:=true;exit;end;

end;

end;

procedure process2;

var i,j:longint;

begin

d[0]:=1;

for i:=1 to v do

begin

d[i]:=0;

truoc2[i]:=0;

end;

for i:=1 to m do

for j:=v downto b[i] do

begin

if (d[j]=0) and (d[j-b[i]]=1) then

begin

background image

68

d[j]:=1;

truoc2[j]:=i;

end;

if d[v]<>0 then begin ok2:=true; exit;end;

end;

end;

procedure xu_li;

begin

ok:=false;

for s:=w to tong do

begin

ok1:=false;

process1;

if ok1 then

begin

ok2:=false;

v:=s-w;

if v=0 then ok2:=true

else

process2;

end;

if ok1 and ok2 then

begin

luu1:=s;

luu2:=v;

ok:=true;

background image

69

break;

end;

end;

end;

procedure inkq;

var i:longint;

begin

assign(f,fo);

rewrite(f);

if ok then

begin

writeln(f,'1');

i:=truoc1[luu1];

while luu1<>a[i] do

begin

write(f,i,' ');

luu1:=luu1-a[i];

i:=truoc1[luu1];

end;

writeln(f,i);

if luu2<>0 then

begin

i:=truoc2[luu2];

while luu2<>b[i] do

begin

write(f,i,' ');

background image

70

luu2:=luu2-b[i];

i:=truoc2[luu2];

end;

write(f,i);

end;

end

else writeln(f,'0');

close(f);

end;

BEGIN

init;

xu_li;

inkq;

END.

Bài 16: Lịch thuê nhân công

Có một dự án kéo dài trong T tháng và ngư ời quản lý cần phải lập lịch sử

dụng công nhân trong dự án, anh ta biết số công nhân tối thiểu cần trong mỗi

tháng. Mỗi khi thuê hay sa thải một công nhân thì đều phải mất một chi phí

xác định, mỗi công nhân được thuê sẽ vẫn nhận được lương tháng ngay cả khi

không sử dụng anh ta làm việc.

Với mỗi công nhân, người quản lý biết chi phí thuê, chi phí sa thải và tiền

lương phải trả cho công nhân đó trong 1 tháng. Và bài toán đ ặt ra như sau: Cần

phải thuê hay sa thải bao nhiêu công nhân mỗi tháng để tổng chi phí dành cho

nhân công của dự án là nhỏ nhất, tức là giảm tối đa chi phí của dự án.

Dữ liệu vào: Đọc từ file văn bản EMPLOY.IN có cấu trúc như sau:

- Dòng đầu ghi T là số tháng diễn ra dự án (T=<100).

background image

71

- Dòng thứ hai ghi 3 số lần lượt là chi phí thuê, lương tháng, chi phí sa th ải

mỗi công nhân.

- Dòng cuối cùng là T số nguyên dương, số thứ i cho biết số công nhân tối

thiểu cần cho tháng thứ i, các giá trị số không quá 150.

Kết quả ra: Ghi ra file văn bản EMPLOY.OUT theo định dạng:

- Dòng thứ nhất ghi tổng chi phí nhỏ nhất tìm được.

- Dòng thứ hai ghi T số, số thứ i là số công nhân hoạt động trong dự án tại

tháng thứ i.

Ví dụ: về file dữ liệu vào và file kết quả ra:

EMPLOY.IN

EMPLOY.OUT

3

4 5 6

10 9 11

265

10 10 11

* Hướng dẫn:

Đề bài này hơi rắc rối khó hiểu một chút vì vậy cần phải đọc kỹ, nắm

chắc. Rõ ràng việc thuê thêm hay sa thải công nhân đều phải chịu phí tổn nên

thấy: để đảm bảo chi phí ít nhất cho việc thuê nhân công trong cả dự án thì số

công nhân đang thuê trong một tháng không nhất thiết phải là số công nhân tối

thiểu cần cho tháng đấy. Ví dụ, phí tổn để thuê cũng như sa thải công nhân rất

lớn thì không dại gì ta lại thường xuyên thuê rồi lại sa thải người trong từng

tháng, tốt nhất nên giữ họvà trả lương (dù họ không làm gì). Nhiệm vụ của

chúng ta trong mỗi tháng phải quyết định có bao nhiêu công nhân trong biên

chế thuê, nghĩa là phải quyết định xem cần thuê hay cần sa thải bao nhiêu công

nhân trong biên chế của tháng trước. Nếu gọi T_max là số công nhân của tháng

cần nhiều người nhất thì rõ ràng số công nhân trong biên chế thuê của một

tháng bất kỳ trong dự án không bao giờ vượt quá T_max. Xét một tháng nào đó,

background image

72

biết rằng không nên sử dụng quá T_max người và cũng không được phép sử

dụng ít hơn số người tối thiểu cần cho tháng đấy, nhưng phải chọn giá trị nào

trong khoảng giới hạn này. Đến đây nếu các giá trị của T và T_max là nhỏ thì có

thể duyệt tìm ra kết quả tối ưu nhưng do cácgiá trị này lại có thể khá lớn nên

buộc phải tìm cách khác hiệu quả hơn.

Lập mảng Scn[1..T], Scn[i] cho biết số công nhân tối thiểu cần cho tháng

thứ i, i=1..T(mảng này nhập vào từ file input). Lập thêm mảng C[T,T_max]

trong đó C[i,j] cho biết chi phí tối thiểu của i tháng đầu tiên của dự án nếu tại

tháng thứ i có j công nhân trong biên chế thuê (i=1..T, =1..T_max). Thấy là giá

trị C[i,j] cóthể xác định thông qua các giá trị C tại tháng i-1 (là tháng trước):

C[i,j] = Min{C[i-1,k] + chi phí để từ k người thành j người }

( i=1..T;j=Scn[i]..T_max; k=Scn[i -1]..T_max)

Bài toán đã đơn giản hơn nhiều khi có được công thức truy hồi và vì độ

phức tạp tính toán không lớn nên chắc chắn chương trình sẽ ngắn gọn, cho kết

quả tối ưu trong thời gianrất ngắn. Kết quả tối ưu cuối cùng là:

Kq = Min{C[T,j]+ chi phí sa thải j người}

(j=Scn[T]..T_max)

Để có thể đưa ra kết quả đầy đủ(số công nhân sử dụng mỗi tháng) thì cần

thêm mảng hai chiều Pre, trong đóPre[i,j] := k, v ới i=1..T; j=Scn[i]..T_max; k

thoả mãn tổng (C[i-1,k] + chi phí để từ k người thành j người) nhỏ nhất. Việc

lần ngược tìm kết quả trong mảngPre không khó.

Bài 17: Cắt hình chữ nhật

Ta cần cắt một hình chữ nhật có kích thước M x N (M, N nguyên dương

không lớn hơn 100) thành một số ít nhất các hình vuông có kích thước nguyên

dương và có cạnh song song với cạnh của hình chữ nhật ban đầu. Biết rằng khi

background image

73

cắt một hình chữ nhật bất kỳ chỉ cắt được theo 1 phương song song với một

trong các các cạnh của hình chữ nhật đó.

Dữ liệu vào: Đọc từ file văn bản CUT.INP có một dòng ghi 2 số M, N.

Kết quả ra: Ghi ra file CUT.OUT có cấu trúc như sau:

Dòng thứ nhất ghi số K là số hình vuông ít nhất cắt ra.

Dòng thứ 2 ghi kích thước của K hình vuông cắt ra được, mỗi số cách nhau

1 dấu cách.

Ví dụ:

CUT.INP CUT.OUT

5 6

5

2 2 2 3 3

Mô tả trên hình vẽ: (Số ghi trong hình vuông cho biết kích thước của hình

vuông đó)

*Hướng dẫn:

+ Nếu M = 1 thì cắt được ít nhất bao nhiêu hình vuông thoả mãn?

+ Nếu N = 1 thì cắt được ít nhất bao nhiêu hình vuông thoả mãn?

+ Nếu gọi F[i,j] là số hình vuông ít nhất cắt ra từ hình chữ nhật kích thước

i, j (với 1 < i < M; 1 < j < N) thì công thức QHĐ được xây dựng như thế nào?

Bài toán con cơ sở: F[1,j] := j với mọi j; F[i,1] := i với mọi i

Công thức QHĐ:

Với i := 2, .., m

Với j := 2, .., n

Nếu i = j thì F[i,i] := 1

2

2

2

3

3

background image

74

Trái lại

t1 := min{F[k,j] + F[i-k, j]} với k = 1,2, …, i div 2;

t2 := min{F[i,k] + F[i, j-k]} với k = 1,2, …, j div 2;

F[i,j] := min{t1, t2}

*Chương trình cài đặt:

program cut_hcn;

const fi='cut.inp';

fo='cut.out';

maxn=1001;

var f:text;

m,n,t:integer;

q:array[1..maxn,1..maxn] of integer;

procedure nhap;

var i,j:integer;

begin

assign(f,fi);

reset(f);

readln(f,m,n);

for i:=1 to m do

for j:=1 to n do

q[i,j]:=-1;

close(f);

end;

procedure xuli;

var min,l,k,i,j:integer ;

begin

background image

75

for i:=1 to m do q[i,1]:=i;

for i:=1 to n do q[1,i]:=i;

for i:=2 to m do

for j:=2 to n do

if i=j then q[i,j]:=1

else

begin

min:=maxint;

for k:=1 to j div 2 do

if min>q[i,k]+q[i,j -k] then min:=q[i,k]+q[i,j-k] ;

for l:=1 to i div 2 do

if min>q[l,j]+q[i -l,j] then min:=q[l,j]+q[i-l,j] ;

q[i,j]:=min;

end;

{if i=-1 then

if (i mod j =0) or (j mod i =0) then

begin

if i>=j then q[i,j]:=i div j else q[i,j]:=j div i;

end

else

begin

min:=maxint;

for k:=1 to j div 2 do

if min>tinhq(i,k)+tinhq(i,j -k) then min:=tinhq(i,k)+tinhq(i,j -

k) ;

for l:=1 to i div 2 do

background image

76

if min>tinhq(l,j)+tinhq(i-l,j) then

min:=tinhq(l,j)+tinhq(i -l,j) ;

q[i,j]:=min;

end;

tinhq:=q[i,j]; }

end;

procedure timkq(i,j:integer);

var k,l,tg:integer;

begin

if (i mod j =0) or (j mod i =0) then

begin

if i>=j then for tg:=1 to i div j do write(f,j,' ')

else for tg:=1 to j div i do write(f,i,' ');

end

else

begin

for k:=1 to (j div 2) do

if q[i,j]=(q[i,k]+q[i,j-k]) then

begin

timkq(i,k);

timkq(i,j-k);

exit;

end ;

for l:=1 to (i div 2) do

if q[i,j]=(q[l,j]+q[i-l,j]) then

begin

background image

77

timkq(l,j);

timkq(i-l,j);

exit;

end;

end;

end;

procedure xuat;

var j,i:integer;

begin

assign(f,fo);

rewrite(f);

writeln(f,q[m,n]);

timkq(m,n);

close(f)

end;

BEGIN

nhap;

xuli;

xuat;

END.

Bài 18: Trò chơi với băng số: Rate This

Trò chơi với băng số là trò chơi tham gia trúng thưởng được mô tả như

sau: Có một băng hình chữ nhật được chia ra làm n ô vuông, đánh số từ trái

qua phải bắt đầu từ 1. Trên ô vuông thứ i người ta ghi một số nguyên dương a

i

,

i = 1, 2, …, n. Ở một lượt chơi, người tham gia trò chơi được quyền lựa chọn

một số lượng tùy ý các ô trên băng số. Giả sử theo thứ tự từ trái qua phải,

background image

78

người chơi lựa chọn các ô i

1

, i

2

, …, i

k

. Khi đó điểm số mà người chơi đạt được

sẽ là:

a

i1

– a

i2

+ … + (-1)

k-1

a

ik

Yêu cầu: Hãy tính số điểm lớn nhất có thể đạt được từ một lượt chơi.

Dữ liệu vào: Đọc từ file văn bản Rate.inp có cấu trúc:

Dòng đầu tiên chứa số nguyên dương n ( n ≤ 10

6

) là số lượng ô của băng

số;

Dòng thứ hai chứa n số nguyên dương a

1

, a

2

, …, a

n

( a

i

≤ 10

4

, i = 1, 2, …, n )

ghi trên băng số. Các số liên tiếp trên cùng dòng được ghi cách nhau bởi ít nhất

một dấu cách.

Kết quả ra: Ghi ra file văn bản Rate.out có cấu trúc là:

Một số nguyên duy nhất là số điểm lớn nhất có thể đạt được từ một lượt

chơi.

Ví dụ:

Rate.inp

Rate.out

7

4 9 2 4 1 3 7

17

Ràng buộc: 60% số tests ứng với 60% số điểm của bài có 1 ≤ n ≤ 20.

*Chương trình cài đặt:

program rate_this;

const fi='ratethis.inp';

background image

79

fo='ratethis.out';

maxn=1000000;

var f:text;

n,kq,dem:int64;

a,fl,fc,ds:array[1..maxn] of int64;

luuc,luul:array[1..maxn] of integer;

procedure nhap;

var i:longint;

begin

assign(f,fi);

reset(f);

readln(f,n);

for i:=1 to n do

read(f,a[i]) ;

close(f);

dem:=0;

fillchar(luul,sizeof(luul),0);

fillchar(luuc,sizeof(luuc),0);

end;

function max2so(cl,j,x,y:int64):int64;

begin

if x>y then max2so:=x

else begin

max2so:=y;

background image

80

if cl=0 then luuc[j]:=1;

if cl=1 then luul[j]:=1;

end;

end;

procedure xuli;

var i,chanle:longint;

begin

fl[1]:=a[1];

fc[1]:=0;

for i:=2 to n do

begin

fl[i]:=max2so(1,i,fl[i-1],fc[i-1]+a[i]);

fc[i]:=max2so(0,i,fc[i-1],fl[i-1]-a[i]);

end;

if fl[n]>fc[n] then begin kq:=fl[n];chanle:=1;end

else begin kq:=fc[n];chanle:=0;end;

for i:=n downto 1 do

begin

if (chanle=1) and (luul[i]=1) then

begin

inc(dem);

ds[dem]:=i;

chanle:=0;

continue;

end;

background image

81

if (chanle=0) and (luuc[i]=1) then

begin

inc(dem);

ds[dem]:=i;

chanle:=1;

end;

end;

end;

procedure xuat;

var i:longint;

begin

assign(f,fo);

rewrite(f);

writeln(f,kq);

for i:=dem downto 1 do

write(f,ds[i],' ');

close(f)

end;

BEGIN

nhap;

xuli;

xuat;

END.

background image

82

Bài 19: Con kiến

Trên một sân hình chữ nhật MxN, được chia thành các ô vuông đơn v ị,

mỗiô chứa một lượng thức ăn. Một con kiến xuất phát từ ô (1,1) muốn đi qua

sân để đến dòng thứ M. Con kiến chỉ có thể đi theo một dòngchia nhỏ trên sân

ứng với một dòng của bảng chữ nhật hoặc đi theotrên một cột của sân. Hãy chỉ

ra đường đi giúp con kiến có được nhiều thức ăn nhất.

Dữ liệu vào: Đọc từ file văn bản FOOD.INP có cấu trúc:

Dòng đầu là 2 số M, N.

M dòng tiếp theo, mỗi dòng có N số tương ứng là lượng thức ăn có trong

M x N ô trên sân.

Kết quả ra: Ghi ra file văn bản FOOD.OUT có cấu trúc:

Dòng đầu là tổng lượng thức ăn nhiều nhất mà con kiến có thể ăn được.

Các dòng tiếp theo, mỗi dòng là 2 số tương ứng là toạ độ dòng, cột của ô

mà con kiến đi qua.

Ví dụ:

FOOD.INP

3 5

FOOD.OUT

45

1 1

2 1

2 2

2 3

background image

83

3 3

*Hướng dẫn:

Trọng số ở đây là lượng thức ăn trên mỗi ô.

Quy tắc đi:

Dễ dàng tìm được công thức quy hoạch động là:

Gọi B[i,j] là lượng thức ăn lớn nhất đi từ ô (1,1) đến ô (i,j)

B[1,j]= A[1,j] với j = 1..N

B[i,1]= A[i,1]+B[i-1,1] với i = 2..M

B[i,j]=Max{B[i-1,j],B[i,j-1]} + A[i,j] với i = 2..M và j = 2..N

Bài 20: Sa mạc

Một bãi sa mạc có dạng hình chữ nhật MxN. Mỗi ô vuông đơn vị trên sa

mạc có một độ cao nào đó. Một người muốn đi từ bờ đầu này sang bờcuối

cùng bên kia. Người đó chỉ có thể đi từ ô đang đứng tới mộtô mới theo hướng

thẳng đứng chéo trái hoặc chéo phải. Giả thiết rằng người đó không được

vượt ra hai mép trái và phải của sa mạc.

Hãytìm đường đi sao cho người đó phải vượt qua quãng đường ngắn

nhất. Mỗi lần đi từ một ô sang ô mới tiếp theo người đó phải đi hết quãng

đường bằng độ chênh cao giữa hai ô đó.

background image

84

SAMAC.INP

SAMAC.OUT

12(Quãng đường Min)

(1,3)(2,4) (3,3) (4,2) (5,2)

*Hướng dẫn:

Trọng số là độ chênh cao giữa hai ô liên tiếp.

Quy tắc đi.

Công thức tối ưu:

B[i,j] là quãng đường nhỏ nhất đi từ bờ đầu tiên đến ô (i,j).

B[1,j]= 0 với j = 1..N

B[i,1]= Min { B[i-1,1] + abs(A[i,1] - A[i-1,1]), B[i-1,2]+ abs(A[i,1] - A[i-1,2])}

Với i = 2..M

B[i,j]= Min { B[i-1,j -1] + abs(A[i,j] - A[i-1,j -1]),B[i-1,j] + abs(A[i,j] - A[i-1,j]),

B[i-1,j+1] + abs(A[i,j] - A[i-1,j+1])} Với i = 2..M, j = 2..N-1

B[i,N] = Min { B[i-1,N] + abs(A[i,N] - A[i-1,N]), B[i-1,N-1]+ abs(A[i,N] - A[i-

1,N-1])}, Với i = 2..M.

background image

85

Bài 21: Quầy bán hàng.

Một siêu thị có M gian hàng, mỗi gian hàng gồm N ngăn chứa, mỗi ngăn

chứa được bố trí ở một phòng. Giám đốc siêu thị quyết định mở một đợt

khuyến mãi cho khách hàng với các quy tắc sau:

Mỗi gian hàng được bố trí trên từng tấng tương ứng từ tầng 1 đến M.

Mỗi tầng có N thang máy đi lên ứng với mỗi phòng.

Một khách hàng có thể mua sản phẩm tại một gian hàng nhưng chỉ có thể

đi theo một hướng (không được mua xong rồi quay trở lại nơi đã mua).

Khách hàng có thể đi thang máy lên tầng tiếp theo, nhưng phải mua ít nhất tại

một ngăn chứa ở tầng đó thì mới được phép đi lên tầng trên nữa.

Khách hàng mua hàng tại một ngăn chứa. Mỗi ngăn chứa quy định một số

lượng hàng mà người khách buộc phải mua khi đến ngăn chứa đó.

Nếu độ chênh số lượng hàng giữa hai ngăn chứa liên tiếp của một khách hàng

là một số may mắn đã biết trước. Khách hàng đó sẽ được khuyến mãi thêm

một số hàng bằng chính số may mắn đó.

Đến tầng thứ M, khách hàng chỉ có thể mua hàng tại duy nhất một ngăn

chứa.

Hãy giúp khách hàng lựa chọn điểm xuất phát và hướng đi sao cho mua

được nhiều hàng nhất (Kể cả số hàng được khuyến mãi).

Dữ liệu đầu vào cho trong FILE văn bản SHOP.INPcó cấu trúc như sau:

Dòng đầu là 3 số M,N, K (1<M, N, K ≤100), K là số các con số may mắn.

Dòng thứ hai ghi K con sốmay mắn.

M dòng tiếp theo ghi số lượng hàng quy định tại mỗi ngăn chứa. Mỗi

dòng gồm N số cách nhau bởi ít nhất một dấu trắng.

Kết quả ghi ra FILE văn bản SHOP.OUT như sau:

Dòng một là sốlượng hàng nhiều nhất.

background image

86

Dòng hai điểm xuất phát và quá trình mua hàng. Mỗi ngăn chứa đi qua

được biểu diễn theo dạng ″(x,y) ″ trong đó (x,y) là vị trí của ngăn chứa.

Ví dụ:

SHOP.INP

SHOP.OUT

80 (Lượng hàng mua được Max)

(1,3) (1,2) (2,2) (2,1) (3,1) (3,2) (3,3) (3,4) (4,4)

*Hướng dẫn:

Trọng số ở đây là số lượng hàng tại các ngăn chứa. Đồng thời khi thoả

mãn điều kiện ″khuyến mãi ″ thì trọng số sẽ được tăng thêmsố lượng bằng số

các con số may mắn (phụ thuộc vào ô đứng trước).

Quy tắc đi:

:

Công thức quy hoạch động:

B[i,j] là lượng hàng max khi đi t ừ tầng 1 cho đến ngăn chứa (i,j)

B[0,j]= 0 với j =1..N

B[i,0]= 0 với i =1..M

background image

87

B[i,j]= Max{B[i,j-1]+KM1, B[i-1,j] + KM2, B[i-1,k]+ SA[i,u]+KM3} + A[i,j]. Với

i

=1..M

,

j

=1..(N -1)

,

k

=(j+1)..M

,

u

=

j+1..k

KM1 là lượng hàng khuyến mãi nếu Abs(A[i,j]-A[i,j-1]) là con số may mắn.

KM2 là lượng hàng khuyến mãi nếu Abs(A[i,j]-A[i-1,j]) là con số may mắn.

KM3 là tổng số lượng hàng khuyến mãi nếu Abs(A[i,k]-A[i-1,k]) vàAbs(A[i,t]-

A[i,t+1])

với

t

=

j..(u-1)

các

con

số

may

mắn.

B[i,N]= Max{B[i,N-1]+KM1,B[i-1,N]+KM2}+A[i,N]

Với i =1..M

KM1 là lượng hàng khuyến mãi nếu abs(A[i,N]-A[i,N-1]) là con số may

mắn.

KM2 là lượng hàng khuyến mãi nếu abs(A[i,N]-A[i-1,N]) là con số may mắn.

Nhận xét:

Còn rất nhiều bài toán khác có dạng như một trong các bài toán cơ bản

này nhưng chung quy lại chúng ta đều có thể đưa nó về một dạng chung. Sau

đó dựa vào những nguyên tắc giải chung, ta đều có thể giải quyết dễ dàng.

Các dạng bài toán tổng quát này khi dữ liệu cho quá giới hạn khai báo bảng hai

chiều đều có thể giải quyết bằng cách quy hoạch liên tục trên 2mảng một

chiều. Sau mỗi bước quy hoạch phải thay đổi 2 mảng này saocho phù hợp với

bước quy hoạch tiếp theo. Cái khó của bài toán có dữ liệu lớn này là việc lưu

trữ trạng thái để sau khi quy hoạch toàn bộ ta còn có thể in ra file kết quả ″quá

trình đi ″ của phương án tối ưu.

background image

88

Chương IV.

Một số đề tự giải

Bài 22: Chia thành nhiều nhóm có tổng bằng nhau

Cho n số a

1

, a

2

,..., a

n

(n ' NI*). Tìm cách chia số trên thành m nhóm sao cho

mỗi nhóm đều có tổng bằng nhau và m tìm được là lớn nhất.

Dữ liệu vào: Đọc vào từ file văn bản MNHOM.INP có cấu trúc như sau :

Dòng đầu ghi số n nguyên dương (N <= 10

4

)

Các dòng tiếp theo ghi n số a

1

, a

2

,..., a

n

, các số cách nhau ít nhất 1 dấu

cách;

Kết quả ra: Ghi ra file văn bản: MNHOM.OUT có cấu trúc như sau:

Dòng đầu tiên ghi số m lớn nhất tìm được

M dòng tiếp theo, mỗi dòng ghi các chỉ số của các phần tử thuộc cùng 1

nhóm;

Ví dụ:

MNHOM.INP

MNHOM.OUT

7

8 2 1 3 9 10 11

4

1 4

2 5

3 6

7

Bài 23: Bài toán xoay DOMINO

Cho N thanh DOMINO xếp theo chiều dọc như hình vẽ.

Ví dụ hình trên gồm 5 thanh DOMINO.

background image

89

Mỗi thanh DOMINO gồm 2 phần, phần trên và phần dưới. Trên mỗi phần

có một số từ 1 đến 6. Yêu cầu đặt ra là hãy tìm cách xoay các thanh (xoay 180

độ) để sau khi xoay chênh lệch giữa tổng trên và tổng dưới là ít nhất. Giới hạn:

N<=1000.

Dữ liệu vào: Đọc từ file văn bản DOMINO.INP có cấu trúc như sau:

-

Dòng đầu tiên ghi số nguyên dương N

-

N dòng tiếp theo, mỗi dòng i ghi 2 số nguyên a

i

, b

i

(1 <=a

i

,b

i

<= 6)

tương ứng là số ở trên, ở dưới của thanh DOMINO thứ i;

Kết quả ra: Ghi ra file văn bản DOMINO.OUT có cấu trúc như sau:

-

Dòng đầu tiên ghi giá trị chênh lệch giữa tổng trên và tổng đưới;

-

Các dòng tiếp theo là chỉ số của các thanh DOMINO đ ược xoay;

Ví dụ:

DOMINO.INP

DOMINO.OUT

5

1 2

1 6

3 5

2 6

6 4

0

1

Bài 24: Bài toán ngân hàng trả tiền;

Một người đi lấy tiền ở một ngân hàng. Anh ta cần lấy một khoản đúng M

đồng. Ngân hàng có N đồng tiền A

1

, A

2

,.., A

N

. Hỏi ngân hàng có bao nhiêu cách

trả tiền.

Dữ liệu vào: Đọc từ file văn bản "MONEY.INP" có dạng:

Dòng đầu là hai số N và M (N <=100, M <= 10000)

Các dòng tiếp theo là các phần tử của mảng A.

background image

90

Kết quả ra: Ghi ra file văn bản "MONEY.OUT" gồm một dòng duy nhất là

số cách trả tiền (Số cách trả tiền < Maxlongint)

Ví dụ:

Bài 25: Bài toán dãy có tổng chia hết cho k (đề thi toàn Quốc)

Cho một dãy số nguyên A

1

, A

2

,.., A

N

và một số k. Hãy tìm một dãy con

(không nhất thiết phải liên tiếp nhau) dài nhất có tổng các số chia hết cho số k.

Dữ liệu vào: Đọc từ file văn bản "dayso.inp" có dạng:

Dòng 1 gồm 2 số N và k (N<=1000; k<=50)

Các dòng tiếp theo chứa các số của mảng A.

Kết quả ra: Ghi ra file văn bản "dayso.out" gồm một dòng ghi số phần tử

lớn nhất tìm được.

Ví dụ:

Bài 26: Xếp đồ vật vào ba lô (mỗi loại đồ vật có lýợng không hạn

chế);

Một chiếc ba lô có thể chứa được một khối lượng không quá W. Có N loại

đồ vật được đánh số từ 1 đến N, mỗi đồ vật loại i có khối A

i

và giá trị sử dụng C

i

(i=1, 2, …, N), số lượng đồ vật mỗi loại là không hạn chế. Hãy chọn các đồ vật

(mỗi loại bao nhiêu cái) để xếp vào ba lô sao cho tổng giá trị các đồ vật trong

ba lô là lớn nhất (nếu có thể xếp được).

Dữ liệu vào: Đọc từ file văn bản BALO.INP có cấu trúc như sau:

Dòng đầu ghi 2 số nguyên dương N, W (0 < N, W < 100).

Dòng i+1, 1 < i < N) ghi 2 số nguyên dương A

i

và C

i

(0<A

i

, C

i

<256)

background image

91

Kết quả ra: Ghi ra file văn bản BALO.OUT có cấu trúc như sau:

Dòng đầu ghi tổng giá trị lớn nhất tìm được.

Từ dòng thứ 2, mỗi dòng ghi 2 số là: số hiệu loại đồ vật được chọn và số

lượng loại đồ vật đó.

Ví dụ:

BALO.INP

BALO.OUT

9 100

60 7

8 45

50 15

9 7

7 68

130 57

1 3

70 123

10 95

965

5 10

9 3

Bài 27: Đổi tiền

Ở đất nước Omega người ta chỉ tiêu tiền xu. Có N loại tiền xu, loại thứ i

có mệnh giá là a

i

đồng. Một người khách du lịch đến Omega du lịch với số tiền

M đồng. Ông ta muốn đổi số tiền đó ra tiền xu Omega để tiện tiêu dùng. Ông ta

cũng muốn số đồng tiền đổi được là ít nhất (cho túi tiền đỡ nặng khi đi đây đi

đó). Bạn hãy giúp ông ta tìm cách đổi tiền.

Dữ liệu: vào trong file văn bản Money.inp có dạng:

-

Dòng đầu là 2 số N và M (N <=100 và M <=10000)

-

Các dòng tiếp theo là N số tương ứng là các mệnh giá a

1

, a

2

, …,a

n

(với 0 < a

i

<= 1000)

background image

92

Kết quả: Ghi ra file văn bản Money.out gồm một dòng duy nhất là số

đồng tiền ít nhất có thể đổi được (Số đồng xu < Maxlongint)

Ví dụ:

Money.inp

Money.out

4 10

1 2 3 4

3

Money.inp

Money.out

5 10

2 4 6 8 9

2

Bài 28:Chia tập

Xét tập chứa các số nguyên từ 1 tới N với N = 4K – 1 hoặc N = 4K. Tập các

số nguyên này có thể chia thành hai tập con có tổng các phần tử bằng nhau. Ví

dụ, với N = 7, ta có 4 cách chia thoả mãn điều kiện trên:

{2,3,4,5} và {1,6,7}

{1,3,4,6} và {2,5,7}

{1,2,5,6} và {3,4,7}

{1,2,4,7} và {3,5,6}

Với N cho trước, hãy tính số cách chia thành 2 tập con có tổng các phần

tử bằng nhau.

Dữ liệu: Vào từ file văn bản SET.INP, mỗi dòng chứa một số nguyên N (N

<= 100);

Kết quả: Đưa ra file văn bản SET.OUT số cách chia, mỗi dòng ứng với 1

dòng của file dữ liệu vào

Ví dụ:

SET.INP

SET.OUT

7

4

background image

93

11

35

Bài 29: Đổi tiền xu

Nước Silverland sử dụng hệ thống tiền xu, trong đó các xu có m ệnh giá là

một số chính phương: 1,4,9,…,289(=17

2

). Với hệ thống này, để trả 10 xu ta có

4 cách:

Trả 10 đồng 1 xu,

Trả 1 đồng 4 xu và 6 đồng 1 xu,

Trả 2 đồng 4 xu và 2 đồng 1 xu,

Trả 1 đồng 9 xu và 1 đồng 1 xu.

Nhiệm vụ của bạn là xác định xem có bao nhiêu cách trả một số tiền cho

trước ở nước Silverland.

Dữ liệu vào: Vào từ file văn bản COIN.INP

Gồm nhiều dòng, mỗi dòng một số nguyên dương không vượt quá 800.

Kết quả ra: Đưa ra file văn bản COIN.OUT là số cách trả ứng với từng

trường hợp.

Ví dụ:

COIN.INP

2

10

30

COIN.OUT
1
4

27

27

27

27

27

background image

94

MỤC LỤC

I.1. Tư tưởng của phương pháp quy hoạch động.........................................................................3

I.1.1. Thuật toán chia để trị ......................................................................................................3
I.1.2. Hệ thức truy hồi ..............................................................................................................4
I.1.3. Lập trình động là gì?.......................................................................................................5
I.1.4. Phương pháp quy hoạch động ........................................................................................ 7

I.2. Các bước thực hiện giải bài toán bằng phương pháp quy hoạch động .................................8

I.2.1. Các bước cơ bản: ............................................................................................................8
I.2.2. Tổ chức cài đặt: ..............................................................................................................9
I.2.3 Khi nào dùng phương pháp quy ho ạch động? .................................................................9
Bài 1: Tìm dãy con không gi ảm nhiều phần tử nhất; ............................................................. 10

Hướng dẫn: ......................................................................................................................... 10
* Chương trình cài đặt:.......................................................................................................11

Bài 2: Dãy con chung dài nh ất;.............................................................................................. 13

Ví dụ: ..................................................................................................................................14
*Hướng dẫn: ....................................................................................................................... 14
*Chương trình cài đặt:........................................................................................................15

Bài 3: Dãy con có tổng bằng S .............................................................................................. 17
Bài 4: Xếp đồ vật vào ba lô (mỗi đồ vật chỉ có 1) .................................................................20

*Hướng dẫn ........................................................................................................................ 21
*Chương trình cài đặt:........................................................................................................22

Chương III: Bài tập chọn lọc .....................................................................................................26

Bài 5: Bố trí phòng họp; ........................................................................................................26

* Hướng dẫn: ...................................................................................................................... 27
* Chương trình cài đặt:.......................................................................................................27

Bài 6: Cho thuê máy tính; ......................................................................................................30

*Hướng dẫn: ....................................................................................................................... 31
* Chương trình cài đặt:.......................................................................................................31

Bài 7: Nối điểm ...................................................................................................................... 34

*Hướng dẫn: ....................................................................................................................... 35
* Chương trình cài đặt:.......................................................................................................35

Bài 8: Dãy con đổi chiều, đối dấu dài nhất; ...........................................................................38

* Hướng dẫn: ...................................................................................................................... 39
* Chương trình cài đặt:.......................................................................................................39

Bài 9: Số phép biến đổi ít nhất ............................................................................................... 43

* Hướng dẫn: ...................................................................................................................... 43
*Chương trình cài đặt:........................................................................................................44

Bài 10: Xâu đối xứng; ...........................................................................................................47

*Hướng dẫn: ....................................................................................................................... 47
*Chương trình cài đặt:........................................................................................................47

Bài 11: Bài toán chia k ẹo .......................................................................................................50

background image

95

*Hướng dẫn: ....................................................................................................................... 50
* Chương trình cài đặt:.......................................................................................................50

Bài 12: Mua cá theo lýợng (Olympic Balkan 2000) ; ............................................................ 54

* Hướng dẫn: ...................................................................................................................... 55
* Chương trình cài đặt:.......................................................................................................55

Bài 13: Điền dấu hỏi vào biểu thức ....................................................................................... 57

*Hướng dẫn: ....................................................................................................................... 57
* Chương trình cài đặt:.......................................................................................................58

Bài 14: Chia thành hai nhóm có tích l ớn nhất (ACM 10690) ................................................61

*Hướng dẫn: ....................................................................................................................... 62
*Chương trình cài đặt:........................................................................................................62

Bài 15: Bài toán mua bán hàng .............................................................................................. 64

* Chương trình cài đặt:.......................................................................................................65

Bài 16: Lịch thuê nhân công ..................................................................................................70

* Hướng dẫn:...................................................................................................................... 71

Bài 17: Cắt hình chữ nhật ......................................................................................................72

*Hướng dẫn:....................................................................................................................... 73
*Chương trình cài đặt:........................................................................................................74

Bài 18: Trò chơi với băng số: Rate This ................................................................................77

*Chương trình cài đặt:........................................................................................................78

Bài 19: Con kiến .................................................................................................................... 82

*Hướng dẫn: ....................................................................................................................... 83

Bài 20: Sa mạc ....................................................................................................................... 83

*Hướng dẫn: ....................................................................................................................... 84

Bài 21: Quầy bán hàng...........................................................................................................85

*Hướng dẫn:....................................................................................................................... 86

Nhận xét: ................................................................................................................................ 87
Bài 22: Chia thành nhiều nhóm có tổng bằng nhau ............................................................... 88
Bài 23: Bài toán xoay DOMINO ........................................................................................... 88
Bài 24: Bài toán ngân hàng tr ả tiền; ...................................................................................... 89
Bài 25: Bài toán dãy có t ổng chia hết cho k (đề thi to àn Quốc) ............................................90
Bài 26: Xếp đồ vật vào ba lô (mỗi loại đồ vật có lýợng không hạn chế); ............................. 90
Bài 27: Đổi tiền ...................................................................................................................... 91
Bài 28:Chia tập ...................................................................................................................... 92
Bài 29: Đổi tiền xu ................................................................................................................. 93

background image

96

Tài liệu tham khảo

1. Trần Đỗ Hùng, Chuyên đề bồi dưỡng HSG Tin học THPT,NXB GD 2007

2. Nguyễn Quý Khang, Bài tập Pascal, NXB QGHN 2002

3. Nguyễn Xuân My, Một số vấn đề chọn lọc trong môn Tin học, NXB GD

2002

4. Nguyễn Xuân My, Bài tập lập trình Pascal, NXB TK 1997

5. Báo TH&NT 1999 - 2006

6. Trang web:

http://www.wikipedia.org

7. Trang web:

http://www.vnoi.info

8. Trang web:

http://www.ddth.com


Wyszukiwarka

Podobne podstrony:
Giáo Trình Unix Và Lập Trình C Nhiều Tác Giả, 65 Trang
Slide kỹ Thuật Lập Trình Nguyễn Thủy Đoan Trang, 20 Trang
Slide Ngôn Ngữ Lập Trình C Nguyễn Đình Thuân, 98 Trang
Giáo Trình Lập Trình Pascal Căn Bản Nhiều Tác Giả, 90 Trang
Slide Lập Thẩm Định Dự Án Đầu Tư xây Dựng Pgs Ts Nguyễn Văn Hiệp
Slide Lập Trình Hợp Ngữ Nhiều Tác Giả, 109 Trang
ĐHBK Bài Giảng Hệ Điều Hành (NXB Hà Nội 2001) Lê Tiến Dũng, 96 Trang
Địa Chất Thủy Văn Nhiều Tác Giả, 153 Trang
Bài Tập Lập Trình C Lương Trần Hy Hiền, 18 Trang
Quy Trình Thiết Kế Kênh Biển Nhiều Tác Giả, 43 Trang
Mẫu Mô Tả Công Việc Và Các Tiêu Chuẩn Đánh Giá Một Lập Trình Viên Nguyễn Trọng Hòa
Slide Ngôn Ngữ Lập Trình Tính Toán Fortran Trần Thùy Dương, 71 Trang
Cơ Học Lý Thuyết (Tóm Tắt Lý Thuyết & Bài Tập Mẫu) Trịnh Anh Ngọc, 71 Trang
ĐHCT Giáo Trình Thực Hành Lập Trình Hệ Thống (NXB Cần Thơ 2008) Nguyễn Hứa Duy Khang, 39 Trang
ĐHMO Lập Dự Toán Xây Dựng Cơ Bản Ths Lương Văn Cảnh, 21 Trang
Quản Lý Dự Án Xây Dựng Nhiều Tác Giả, 66 Trang
Hướng Dẫn Lập Và Quản Lý Chi Phí Đầu Tư Xây Dựng Công Trình Bộ Xây Dựng, 46 Trang
ĐHĐL Giáo Trình Kỹ Thuật Lập Trình Nâng Cao (NXB Đà Lạt 2002) Trần Hoàng Thọ, 108 Trang
Lập Trình Web Động Với PHP và MySQL Phần 1 Tống Phước Khải, 132 Trang

więcej podobnych podstron