Thuật toán

Một phần của tài liệu Phương pháp quy hoạch động và một số ví dụ cụ thể (Trang 30)

4 Bài toán: Tổng củ an số tự nhiên sắp theo trật tự không tăng.

4.4Thuật toán

Ta có phương án đầu tiên của giải thuật Bieudien như sau:

Đệ quy

function Bieudien(i,j: integer):longint; begin if j = 0 then Bieudien:=0 else {j > 0} if i = 0 then {i = 0; j > 0} Bieudien:=1 else {i,j > 0} if i < j then {0 < i < j}

Bieudien:=Bieudien(i,i) else {i >= j > 0}

Bieudien:=Bieudien(i,j-1)+Bieudien(i-j,j); end;

Biểu diễn với số tự nhiên (m = 7) và 4 số tự nhiên (n = 4). Thí dụ, hàm Bieudien(1,1) sẽ được gọi 9 lần,… Tổng số lần gọi hàm Bieudien là 79. 79 lần gọi hàm để sinh ra kết quả 11 là quá tốn kém. Ta có phương án đầu tiên của giải thuật Bieudien như sau:

Khá dễ triển khai nhưng chương trình sẽ chạy rất lâu, bạn hãy thử gọi Bieudien(66,32) để trải nghiệm được điều trên. Muốn thế chúng ta tính sẵn các giá trị của hàm theo các trị của đầu vào khác nhau và điền vào một mảng hai chiều Mang. Mảng Mang được mô tả như sau:

Gioihan

MN = 90;{gioi han tren cua m va n} Kieu

ml1 = array[0..MN ] of longint; ml2 = array[0..mn ] of ml1; var Mang: ml2;

Ta quy ước Mang[i, j] chứa số cách biễu diễn i số tự nhiên cho j số tự nhiên. Theo phân tích của phương án 1, ta có:

• Mang[0, 0] = 1; Mang[i, 0] = 0, với i:=1..m. • Mang[i, j] = Mang[i, i], nếu i < j

• Mang[i, j] = Mang[i, j - 1]+Mang[i - j, j], nếu i ≥ j Từ đó ta suy ra quy trình điền trị vào bảng Mang như sau:

Khởi trị

 Mang[0, 0]:= 1;

 với i:=1..m: Mang[i, 0]:= 0;

Điền bảng: Lần lượt điền theo từng cột j:= 1..n. Tại mỗi cột j ta đặt: với i:=0..j-1: Mang[i,j ]:= Mang[i, i];

 với i:= j..m: Mang[i, j]:= Mang[i, j-1] + Mang[i-j, j];

Nhận kết quả: Sau khi điền bảng, giá trị Mang[m, n] chính là kết quả cần tìm.

Làm tốt 1:Dùng mảng 2 chiều

Mang[i,j] = số cách biễu diễn i số tự nhiên cho j số tự nhiên function Bieudien2(m,n: integer):longint;

var i,j: integer; begin

Mang[0,0 ]:=1;

for i:=1 to m do Mang[i,0]:=0; for j:=1 to n do

begin

for i:=0 to j-1 do Mang[i,j ]:=Mang[i,i ];

for i:=j to m do Mang[i,j ]:=Mang[i,j-1 ]+Mang[i-j,j ]; end;

Bieudien2:=Mang[m,n]; end;

Làm tốt lần 2: Dùng mảng hai chiều chúng ta chỉ có thể tính toán được với dữ liệu nhỏ. Bước cải tiến sau đây khá quan trọng: chúng ta dùng mảng một chiều. Quan sát kĩ quy trình gán trị cho mảng hai chiều theo từng cột chúng ta dễ phát hiện ra rằng cột thứ j có thể được tính toán từ cột thứ j - 1. Nếu gọi c là mảng một chiều sẽ dùng, ta cho số số tự nhiên tăng dần bằng cách lần lượt tính j bước, với j:= 1..n. Tại bước thứ j, c[i] chính là số cách biễu diễn i số tự nhiên cho j số tự nhiên. Như vậy, tại bước thứ j ta có:

• c[i] tại bước j = c[i] tại bước (j – 1), nếu i < j. Từ đây suy ra đoạn c[0..(j – 1)] được bảo lưu.

• c[i] tại bước j = c[i] tại bước (j – 1) + c[i – j] tại bước j, nếu i ≥ j.

Biểu thức thứ hai cho biết khi cập nhật mảng c từ bước thứ j – 1 qua bước thứ j ta phải tính từ trên xuống, nghĩa là tính dần theo chiều tăng của i:= j...m.

Mảng c được khởi trị ở bước j = 0 như sau: • c[0] = 1; c[i] = 0, với i:= 1..m.

Với ý nghĩa là, nếu có 0 số tự nhiên thì biễu diễn 0 số tự nhiên cho 0 số tự nhiên sẽ được quy định là 1. Nếu số số tự nhiên m khác 0 thì biễu diễn m số tự nhiên cho 0 số tự nhiên sẽ được 0 phương án.

Ta có phương án 3, dùng một mảng một chiều c như sau:

Làm tốt lần 3: dùng mảng 1 chiều c.

Tại bước thứ j, c[i ] = số cách biễu diễn i số tự nhiên cho j số tự nhiên.

function Bieudien1(m,n: integer):longint; var i,j: integer;

fillchar(c,sizeof(c),0); c[0 ]:=1;

for j:=1 to n do

for i:=j to m do c[i ]:=c[i ]+c[i-j ]; Bieudien1:=c[m ];

end

Một phần của tài liệu Phương pháp quy hoạch động và một số ví dụ cụ thể (Trang 30)