Bài toán di chuyển của Robot

Một phần của tài liệu ỨNG DỤNG PHƯƠNG PHÁP QUY HOẠCH ĐỘNG ĐỂ GIẢI CÁC BÀI TOÁN TỐI ƯU TỔ HỢP 10600755 (Trang 26)

5. Bố cục đề tài

2.1.Bài toán di chuyển của Robot

2.1.1. Phát biểu bài toán

Cho một bảng ô vuông m dòng, n cột (2 ≤ m, n ≤ 30) các ô ghi các số là 0 hoặc 1. Tìm đường đi của Robot, từ góc trái trên (ô (1, 1)) xuống góc phải dưới (ô (m, n)) theo nguyên tắc chỉ được dịch chuyển sang phải và xuống dưới sao cho các số trên đường đi tạo thành số nhị phân có giá trị lớn nhất.

Dữ liệu vào: Ghi vào tệp tin văn bản Robot.inp gồm:

- Dòng đầu tiên ghi giá trị m và n.

- m dòng tiếp theo, trên mỗi dòng ghi n số 0 hoặc 1 các số này cách nhau 1 khoảng trắng.

Dữ liệu ra: Ghi vào tệp tin văn bản Robot.out gồm:

- Dòng đầu tiên ghi giá trị thập phân của số nhị phân được tạo thành ở trên. - Các dòng tiếp theo ghi tọa độ các bước đi (dòng ghi trước, cột ghi sau). Ví dụ: Robot.inp Robot.out 5 7 1 0 0 1 1 0 1 0 1 0 1 0 1 1 1373 1 1 1 2

1 0 1 0 0 1 0 0 1 0 1 1 0 0 1 0 1 0 1 0 1 2 2 2 3 2 4 3 4 4 4 4 5 5 5 5 6 5 7

2.1.2. Phân tích, xử lý bài toán và lập hệ thức truy hồi

Để tìm đường đi tạo thành số nhị phân có giá trị lớn nhất bắt đầu từ ô (1, 1) đến ô (m, n) thì giá trị thập phân tính đến khi Robot đứng tại ô (i, j) phải bắt nguồn từ ô (i – 1, j) hay ô (i, j – 1), có giá trị thập phân tính đến khi Robot đứng tại ô đó lớn hơn giá trị ô còn lại.

Khi thêm một chữ số 0 hoặc 1 vào cuối một số nhị phân thì ta sẽ được giá trị thập phân mới là:

(giá trị cũ) x 2 + (số thêm)

Ví dụ:

Ta có:

TH1: Thêm 1 vào B Đặt B1 = 1011 B1 = 1 x 23 + 0 x 22 + 1 x 21 + 1 x 20 = 2 x (1 x 22 + 0 x 21 + 1 x 20) + 1 x 20 = 2 x B + 1 = 2 x 5 + 1 = 11 TH2: Thêm 0 vào B Đặt B0 = 1010 B0 = 1 x 23 + 0 x 22 + 1 x 21 + 0 x 20 = 2 x (1 x 22 + 0 x 21 + 1 x 20) + 0 x 20 = 2 x B + 0 = 2 x 5 = 10 2.1.3. Thuật toán

Khởi tạo mảng hai chiều có số dòng là m và số cột là n, giá trị các phần tử tương ứng là các số ở dòng m và các cột n tiếp theo được đọc vào từ file.

Khởi tạo mảng hai chiều khác có số dòng là m và số cột là n, giá trị của mỗi phần tử được tính theo công thức:

max (giá trị thập phân ô phía trên, giá trị thập phân ô bên trái) x 2 + (giá trị nhị phân tương ứng)

Ngoại lệ với mảng hai chiều thập phân:

- Đối với các phần tử thuộc hàng 1 thì không có các phần tử ở hàng 0 để tính giá trị.

- Tương tự, đối với các phần tử ở cột 1 thì không có các phần tử ở cột 0 để tính giá trị.

Vì vậy, phải khởi tạo các phần tử ở hàng 0 và cột 0 với giá trị là -1 (để không ảnh hưởng đến kết quả tính toán giá trị thập phân). Riêng ô (0, 1) hoặc (1, 0) cần đặt giá trị 0 để bắt đầu tính ô (1, 1).

Truy vết, sử dụng thuật toán đệ quy trên mảng hai chiều thập phân để tìm đường đi Robot tạo thành số nhị phân lớn nhất từ ô (m, n) cho đến ô (1, 1):

- Nếu giá trị thập phân tính cho đến khi Robot đứng tại ô (m – 1, n) lớn hơn giá trị thập phân tính cho đến khi Robot đứng tại ô (m, n – 1) thì tiếp tục đệ quy từ ô (m – 1, n).

- Ngược lại, đệ quy từ ô (m, n – 1).

2.1.4. Thiết kế chương trình

B1: Khởi tạo mảng binary hai chiều với số dòng và số cột lần lượt là m, n và

giá trị các phần tử tương ứng là các số ở dòng m và các cột n tiếp theo được đọc vào từ file Robot.inp.

B2: Khởi tạo mảng decimal hai chiều với số dòng và số cột lần lượt là m, n

- Các phần tử ở dòng 0 và cột 0 được khởi tạo giá trị là -1.

- Phần tử ở dòng 1, cột 0 hoặc dòng 0, cột 1 được khởi tạo giá trị là 0. - Các phần tử còn lại được tính như sau: (adsbygoogle = window.adsbygoogle || []).push({});

For i từ 1 đến m For j từ 1 đến n

If decimal[i - 1][j] >= decimal[i][j - 1]

decimal[i][j] = decimal[i - 1][j] x 2 + binary[i][j]

Else

decimal[i][j] = decimal[i][j - 1] x 2 + binary[i][j]

B3: Ghi giá trị phần tử decimal[m][n] (giá trị thập phân của độ dài đường đi

tạo thành số nhị phân lớn nhất) vào file Robot.out.

B4: Truy vết, sử dụng thuật toán đệ quy theo phần tử (m, n) thuộc mảng

decimal.

If m = 1 và n = 1

Ghi m, n vào file Robot.out.

Else

If giá trị phần tử (m – 1, n) > giá trị phần tử (m, n -1)

Đệ quy theo phần tử (m – 1, n)

Đệ quy theo phần tử (m, n – 1) Ghi m, n vào file Robot.out.

Kết thúc. 2.1.5. Ví dụ

Ta có mảng binary gồm 6 dòng, 8 cột với các phần tử được đọc vào từ file Robot.inp:

Khởi tạo mảng decimal gồm 6 dòng, 8 cột với:

- Các phần tử ở dòng 0 và cột 0 được khởi tạo giá trị là – 1.

- Phần tử ở dòng 1, cột 0 hoặc dòng 0, cột 1 được khởi tạo giá trị là 0. - Các phần tử còn lại được khởi tạo theo công thức:

decimal[i][j] = max(decimal[i - 1][j], decimal[i][j - 1]) x 2 + binary[i][j]

0 1 2 3 4 5 6 7 0 1 1 0 0 1 1 0 1 2 0 1 0 1 0 1 1 3 1 0 1 0 0 1 0 4 0 1 0 1 1 0 0 5 1 0 1 0 1 0 1

Đệ quy để tìm đường đi tạo thành số nhị phân lớn nhất từ phần tử (5, 7). - Xét ô (4, 7) < ô (5, 6) (684 < 686), tiếp tục đệ quy theo phần tử (5, 6). - Xét ô (4, 6) < ô (5, 5) (342 < 343), tiếp tục đệ quy theo phần tử (5, 5). - Xét ô (4, 5) > ô (5, 4) (171 > 170), tiếp tục đệ quy theo phần tử (4, 5). Tiếp tục quá trình và dừng lại tại phần tử (1, 1).

=> Đường đi của Robot tạo thành số nhị phân lớn nhất: (1, 1), (1, 2), (2, 2), (2, 3), (2, 4), (3, 4), (4, 4), (4, 5), (5, 5), (5, 6), (5, 7).

2.2. Bài toán balo 2.2.1. Phát biểu bài toán 2.2.1. Phát biểu bài toán

Một kẻ trộm đột nhập vào một cửa hiệu tìm thấy có n mặt hàng có trọng lượng p và giá trị v khác nhau, nhưng hắn chỉ mang theo một balo có sức chứa về trọng lượng tối đa là M. Vậy kẻ trộm nên bỏ vào balo những món nào để đạt giá trị lớn nhất trong khả năng mà hắn có thể mang đi được.

0 1 2 3 4 5 6 7 0 -1 -1 -1 -1 -1 -1 -1 1 0 1 2 4 9 19 38 77 2 -1 2 5 10 21 42 85 171 3 -1 5 10 21 42 84 171 342 4 -1 10 21 42 85 171 342 684 5 -1 21 42 85 170 343 686 1373

Dữ liệu vào: Ghi trong tệp tin văn bản Balo.inp gồm:

- Dòng đầu tiên ghi số mặt hàng n và M trọng lượng Balo của tên trộm cách nhau một khoảng trắng.

- n dòng tiếp theo, trên mỗi dòng ghi p và v mỗi mặt hàng, các số này cách nhau một khoảng trắng. (adsbygoogle = window.adsbygoogle || []).push({});

Dữ liệu ra: Ghi trong tệp tin văn bản Balo.out gồm:

- Dòng đầu tiên ghi giá trị lớn nhất trong việc chọn n mặt hàng với trọng lượng tối đa là M.

- Các dòng tiếp theo ghi số thứ tự các mặt hàng được chọn. Ví dụ: Balo.inp Balo.out 5 6 1 2 3 2 3 3 2 4 1 1 9 1 3 4

2.2.2. Phân tích, xử lý bài toán và lập hệ thức truy hồi

Ta chia nhỏ bài toán thành các bài toán con với các mặt hàng {1, 2… i} (1 ≤ i ≤ n) và trọng lượng tối đa của Balo là j (1 ≤ j ≤ M).

Với trọng lượng tối đa là j thì việc chọn các mặt hàng từ {1, 2 …i} sẽ có 2 khả năng:

- Nếu không chọn mặt hàng thứ i thì giá trị lớn nhất có thể bằng cách chọn các mặt hàng từ {1, 2… i – 1} với trọng lượng tối đa là j.

v(i, j) = v(i - 1, j)

- Nếu chọn mặt hàng thứ i thì giá trị lớn nhất có thể bằng tổng giá trị của nó và giá trị lớn nhất trong việc chọn các mặt hàng {1, 2… i – 1} với trọng lượng tối đa là j – pi.

v(i, j) = vi + v(i - 1, j - pi)

2.2.3. Thuật toán

Khởi tạo mảng hai chiều có số dòng n và số cột là M, giá trị mỗi phần tử là giá trị lớn nhất trong việc chọn các mặt hàng từ {1, 2… i} với trọng lượng tối đa là j, được tính theo công thức:

v(i, j) = v (i - 1, j) hoặc

v(i, j) = vi + v(i - 1, j - pi) Ngoại lệ đối với các phần tử ở hàng 0 và cột 0:

- v(0, j) là giá trị lớn nhất trong việc chọn 0 mặt hàng với trọng lượng tối đa là j => v(0, j) = 0.

- v(i, 0) là giá trị lớn nhất trong việc chọn i mặt hàng với trọng lượng tối đa là 0 => v(i, 0) = 0.

Truy vết, sử dụng thuật toán đệ quy trên mảng hai chiều (n, M) để tìm các mặt hàng được chọn, bắt đầu từ mặt hàng thứ n và sau đó giảm dần đến mặt hàng thứ i.

- Nếu giá trị tại ô (n - 1, M - pi) + vi lớn hơn giá trị tại ô (n - 1, M) thì tiếp tục đệ quy từ ô (n - 1, M - pi).

- Ngược lại, đệ quy từ ô (n - 1, M).

2.2.4. Thiết kế chương trình

B1: Khởi tạo mảng something một chiều với độ dài là n và giá trị các phần

tử được đọc vào từ file Balo.inp.

B2: Khởi tạo mảng balo hai chiều với số dòng và số cột lần lượt là n, M

(được đọc vào từ file Balo.inp) và giá trị các phần tử được khởi tạo như sau: - Các phần tử ở dòng 0 và cột 0 được khởi tạo giá trị là 0.

- Các phần tử còn lại được tính như sau:

For i từ 1 đến n

For j từ 1 đến M If pi ≤ j

If vi + balo[i - 1][j - pi] > balo[i - 1][j] balo[i][j] = vi + balo[i - 1][j - pi]

Else

balo[i][j] = balo[i - 1][j] (adsbygoogle = window.adsbygoogle || []).push({});

Else

balo[i][j] = balo[i - 1][j]

B3: Ghi giá trị phần tử balo[n][M] (giá trị lớn nhất trong việc chọn n mặt hàng với trọng lượng tối đa của balo là M) vào file Balo.out.

B4: Truy vết, sử dụng thuật toán đệ quy theo phần tử (n, M) If n = 0 Thoát khỏi hàm Else If pn <= M If vn + giá trị phần tử (n - 1, M – pn) > giá trị phần tử (n - 1, M) Đệ quy theo phần tử (n - 1, M – pn)

Ghi n vào file Balo.out

Else

Đệ quy theo phần tử (n - 1, M)

Else

Đệ quy theo phần tử (n - 1, M)

2.2.5. Ví dụ

Sau khi đọc n mặt hàng từ file Balo.inp và mảng hai chiều balo được khởi tạo và tính toán: Đệ quy để tìm các mặt hàng được chọn từ phần tử thứ (5, 6). Vì p5 ≤ M (1 ≤ 6) => Xét ô (4, 6) > ô (4, 5) + 1 (9 > 7 + 1), tiếp tục đệ quy theo phần tử (4, 6). Vì p4 ≤ M (2 ≤ 6) => Xét ô (3, 6) < ô (3, 4) + 4 (5 < 5 + 4), ghi nhận, tiếp tục đệ quy theo phần tử (3, 4). Vì p3 ≤ M (3 ≤ 4) => Xét ô (2, 4) < ô (2, 1) + 3 (4 < 2 + 3), ghi nhận, tiếp tục đệ quy theo phần tử (2, 1).

Vì p2 ≤ M (3 > 1) => Tiếp tục đệ quy theo phần tử (1, 1).

Vì p1 ≤ M (1 ≤ 1) => Xét ô (0, 1) < ô (0, 0) + 2 (0 < 0 + 2), ghi nhận, tiếp tục đệ quy theo phần tử (0, 0). 0 1 2 3 4 5 6 0 0 0 0 0 0 0 0 1 0 2 2 2 2 2 2 2 0 2 2 2 4 4 4 3 0 2 2 3 5 5 5 4 0 2 4 6 6 7 9 5 0 2 4 6 7 7 9

Vì n = 0 quá trình đệ quy kết thúc. => Các mặt hàng được chọn: 1, 3, 4.

2.3. Bài toán phép nhân tổ hợp dãy ma trận 2.3.1. Phát biểu bài toán 2.3.1. Phát biểu bài toán

Cần tính M = M1 x M2 x … x Mn

Trong đó: Mi là ma trận cấp a[i – 1] x a[i] (i = 1… n). Hãy xác định thứ tự thực hiện các phép nhân sao cho số phép tính là tối thiểu.

Dữ liệu vào: Ghi vào tệp tin văn bản Matrix.inp gồm:

- Dòng đầu tiên ghi số lượng n ma trận (n <= 100).

- Dòng thứ hai ghi n + 1 số nguyên dương a[1], a[2], …, a[n + 1] cách nhau một khoảng trắng.

Dữ liệu ra: Ghi vào tệp tin văn bản Matrix.out gồm:

- Dòng đầu tiên ghi số phép nhân tối thiểu cần thực hiện.

- Dòng thứ hai ghi biểu thức kết hợp tối ưu của phép nhân dãy ma trận. Ví dụ: Matrix.inp Matrix.out 5 2 4 1 3 5 2 37 (m[1].m[2])((m[3].m[4]).m[5])

2.3.2. Phân tích, xử lý bài toán và lập hệ thức truy hồi

Theo ta biết, phép nhân ma trận không có tính chất giao hoán nhưng có tính chất kết hợp.

Nếu dãy chỉ có một ma trận thì số phép nhân là 0, và phép nhân một cặp ma trận có thể tính được ngay, bằng tích số hàng ma trận bên trái, số cột ma trận bên trái với số cột ma trận bên phải.

Ví dụ 1:

Phép nhân hai ma trận A x B với A cấp 3 x 5, B cấp 5 x 4 tạo thành ma trận cấp 3 x 4 với số phép nhân là 3 x 5 x 4 = 60.

Tiếp tục như vậy, ta sẽ tính được số phép nhân của dãy ba ma trận liên tiếp… Cuối cùng thì tính được số phép nhân của n ma trận.

Ví dụ 2: (adsbygoogle = window.adsbygoogle || []).push({});

Phép nhân hai ma trận A x B x C với A cấp 3 x 6, B cấp 6 x 4 và C cấp 4 x 7: Với cách kết hợp (A x B) x C. Phép tính A x B tạo thành ma trận cấp 3 x 4 với số phép nhân là 3 x 6 x 4 = 72, sau đó nhân tiếp với ma trận C tạo thành ma trận cấp 3 x 7 với số phép nhân là 3 x 4 x 7 = 84. Tổng số phép nhân là 156. Với cách kết hợp A x (B x C). Phép tính B x C tạo thành ma trận cấp 6 x 7 với số phép nhân là 6 x 4 x 7 = 168, sau đó được nhân bởi ma trận A tạo thành ma trận 3 x 7 với số phép nhân là 3 x 6 x 7 = 126. Tổng số phép là 294.

Với ví dụ trên, ta thấy rằng tùy thuộc vào cách kết hợp mà được số phép nhân tối thiểu.

Gọi f[i, j] là số phép nhân tối thiểu cần thực hiện để nhân đoạn ma trận liên tiếp từ i đến j. Khi đó f[i, i] = 0 i.

Gọi k là vị trí xen giữa i và j (i ≤ k ≤ j).

Để tính f[i, j] thì có nhiều cách kết hợp, tương ứng với một giá trị k là một trong các cách đó:

f[i, j] = f[i, k] + f[k, j] + a[i].a[k + 1].a[j +1]

Với a[i].a[k + 1].a[j + 1] là số phép nhân của tích các ma trận từ i đến k và các ma trận từ k + 1 đến j.

Suy ra, do có nhiều cách kết hợp, ta cần chọn cách kết hợp để đạt số phép nhân ít nhất:

f[i, j] = min i ≤ k ≤ j (f[i, k] + f[k, j] + a[i].a[k + 1].a[j +1])

2.3.3. Thuật toán

Khởi tạo mảng một chiều có độ dài n + 1 và các giá trị của mảng lần lượt là các số được đọc vào từ file đầu vào.

Khởi tạo mảng hai chiều có số dòng và số cột là n + 1 dùng để lưu trữ các giá trị k (vị trí cần kết hợp).

Khởi tạo mảng hai chiều có số dòng và số cột là n:

- Đối với các phần tử nằm trên đường chéo chính (1, 1), (2, 2)… (n, n) khởi tạo với giá trị là 0.

f[i, j] = min i ≤ k ≤ j (f[i, k] + f[k, j] + a[i].a[k + 1].a[j +1]) + Bắt đầu từ các phần tử ở vị trí số cột = số hàng + 1.

+ Tiếp tục là các phần tử ở vị trí số cột = số hàng + 2. …

+ Cuối cùng, là phần tử ở vị trí (1, n).

Tại trường hợp min, lưu trữ giá trị k vào đúng vị trí của phần tử đó ở mảng hai chiều đầu tiên vừa tạo ở trên.

Truy vết, sử dụng thuật toán đệ quy trên mảng hai chiều đầu tiên (lưu trữ giá trị k) để ghi biểu thức kết hợp tối ưu của phép nhân dãy ma trận.

2.3.4. Thiết kế chương trình

B1: Khởi tạo mảng size một chiều có độ dài n + 1 và các giá trị của mảng

lần lượt là các số được đọc vào từ file Matrix.inp.

B2: Khởi tạo mảng position hai chiều có số dòng và số cột là n. B3: Khởi tạo mảng matrix hai chiều có số dòng và số cột là n:

- Các phần tử ở vị trí (1, 1), (2, 2)… (n, n) được khởi tạo giá trị là 0. - Các phần tử nằm ở vị trí số hàng < số cột được tính như sau:

For len từ 1 đến n - 1

For i từ 1 đến khi i + len = n

j = i + len

leftMatrix = matrix[i][k] rightMatrix = matrix[k + 1][j] tmp = leftMatrix + rightMatrix + size[i].size[k+1].size[j + 1] If matrix[i][j] > tmp matrix[i][j] = tmp position[i][j] = k

B4: Truy vết, sử dụng thuật toán đệ quy theo phần tử (1, n) thuộc mảng position.

If i = j (adsbygoogle = window.adsbygoogle || []).push({});

Ghi mi vào file Matrix.out

Else

k = position[i][j]

Ghi dấu ( vào file Matrix.out Đệ quy theo phần tử (i, k) Ghi dấu . vào file Matrix.out Đệ quy theo phần tử (k + 1, j) Ghi dấu ) vào file Matrix.out

2.3.5. Ví dụ

Khởi tạo mảng matrix gồm 5 dòng, 5 cột với:

- Các phần tử (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) được khởi tạo giá trị là 0. - Các phần tử còn lại được tính theo công thức:

f[i, j] = min i ≤ k ≤ j (f[i, k] + f[k, j] + a[i].a[k + 1].a[j +1])

Từ đó, ta được mảng position như sau:

0 1 2 3 4 5 1 0 8 14 33 37 2 0 12 35 33 3 0 15 25 4 0 30 5 0 0 1 2 3 4 5 1 0 1 2 2 2 2 0 2 2 2 3 0 3 4 4 0 4 5 0

Đệ quy theo phần tử (1, 5) để ghi biểu thức kết hợp tối ưu của phép nhân dãy ma trận:

- Vì 1 ≠ 5 => k = 2, tiếp tục đệ quy theo hai phần tử (1, 2) và (3, 5). + Vì 1 ≠ 2 => k = 1, ghi nhận được phần tử (1, 1) và (2, 2).

+ Vì 3 ≠ 5 => k = 4, tiếp tục đệ quy theo phần tử (3, 4) và ghi nhận phần tử (5, 5).

++ Vì 3 ≠ 4 => k = 1, ghi nhận được phần tử (3, 3) và (4, 4). => Biểu thức tối ưu: (m[1].m[2])((m[3].m[4]).m[5])

2.4. Bài toán lập lịch thuê thợ may hàng tháng 2.4.1. Phát biểu bài toán 2.4.1. Phát biểu bài toán

Có một hợp đồng của đối tác bên Mỹ kéo dài trong t tháng, người quản lý

Một phần của tài liệu ỨNG DỤNG PHƯƠNG PHÁP QUY HOẠCH ĐỘNG ĐỂ GIẢI CÁC BÀI TOÁN TỐI ƯU TỔ HỢP 10600755 (Trang 26)