5.15 Cóc
Một chú cóc máy có thể nhày k bước với độ dài khác nhau (b1, b2, ..., bk) trên đoạn đường thẳng. Đặt cóc trên đoạn đường thắng tại vạch xuất phát 0. Cho biết số cách nhảy để cóc đến được điểm N.
Thí dụ, số bước k = 2, b1 = 2, b2 = 3, đoạn đường dài N = 8.
Có 4 cách: (2,2,2,2) (2, 3, 3) (3, 3, 2) (3, 2, 3).
Thuật toán: Quy hoạch động.
Gọi S(n) là số cách để cóc vượt đoạn đường dài n. Dựa theo bước nhảy đầu tiên ta chia toàn bộ các phương án nhảy của cóc thành k nhóm không giao nhau. Như vậy,
-
Nhóm 1 sẽ gồm các phương án bắt đầu bằng bước nhảy độ dài b1, tức là gồm các phương án dạng (b1,...). Sau bước nhảy đầu tiên, cóc vượt đoạn đường b1, đoạn đường còn lại sẽ là nb1, do đó tổng số phương án của nhóm này sẽ là S(nb1).
-
Nhóm 2 sẽ gồm các phương án bắt đầu bằng bước nhảy độ dài b2, tức là gồm các phương án dạng (b2,...). Sau bước nhảy đầu tiên, cóc vượt đoạn đường b2, đoạn đường còn lại sẽ là nb2, do đó tổng số phương án của nhóm này sẽ là S(nb2).
-
...
-
Nhóm i sẽ gồm các phương án bắt đầu bằng bước nhảy độ dài bi, tức là gồm các phương án dạng (bi,...). Sau bước nhảy đầu tiên, cóc vượt đoạn đường bi, đoạn đường còn lại sẽ là nbi, do đó tổng số phương án của nhóm này sẽ là S(nbi).
-
...
-
Nhóm k sẽ gồm các phương án bắt đầu bằng bước nhảy độ dài bk, tức là gồm các phương án dạng (bk,...). Sau bước nhảy đầu tiên cóc vượt đoạn đường bk, đoạn đường còn lại sẽ là nbk, do đó tổng số phương án của nhóm này sẽ là S(nbk).
Dĩ nhiên, bước đầu tiên là bi sẽ được chọn nếu bi n.
Vậy ta có,
S(n) = { S(nbi) | n bi i = 1, 2, … , k } (*)
Để ý rằng khi đoạn đường cần vượt có chiều dài n = 0 thì cóc có 1 cách nhảy (là đứng yên), do đó S(0) = 1. Ngoài ra ta quy định S(n) = 0 nếu n < 0.
Sử dụng mảng s ta tính dần các trị s[0] = 1; s[1], s[2], ..., s[n] theo hệ thức (*) nói trên. Kết quả được lưu trong s[n].
Độ phức tạp Với mỗi giá trị n ta tính k bước nhảy, vậy độ phức tạp thời gian cỡ k.n.
Các chương trình dưới đây đọc dữ liệu từ input file "coc.inp" gồm các số k, n và độ dài các bước nhảy bi, i = 1, 2, ..., k. Kết quả được hiển thị trên màn hình.
(* Coc.pas *)
uses crt;
const
fn = 'coc.inp';
maxk = 10; maxn = 50;
type mi1 = array[0..maxk] of integer;
ml1 = array[0..maxn] of longint;
var
k, n: integer;
b: mi1;
s: ml1;
procedure Doc;
var f: text; i: integer;
begin
assign(f,fn); reset(f);
read(f,k, n);
for i := 1 to k do read(f,b[i]);
close(f);
end;
procedure TinhS(d: integer);
var i: integer; v: longint;
begin
v := 0;
for i := 1 to k do
if (d >= b[i]) then v := v + s[d-b[i]];
s[d] := v;
end;
function XuLi: longint;
var d: integer;
begin
s[0] := 1;
for d := 1 to n do TinhS(d);
XuLi := s[n];
end;
BEGIN
Doc;
writeln(#13#10, XuLi);
readln;
END.
// DevC++: Coc.cpp
#include
#include
#include
using namespace std;
// D A T A A N D V A R I A B L E
const char BL = ' ';
int b[10];
int s[50];
int n, k;
void Doc();
int XuLi();
void TinhS(int);
int main(){
Doc();
cout << endl << XuLi() << endl;
system("PAUSE");
return EXIT_SUCCESS;
}
void Doc() {
ifstream f("Coc.inp");
f >> k >> n;
for (int i = 1; i <= k; ++i) f >> b[i];
}
int XuLi() {
s[0] = 1;
for (int d = 1; d <= n; ++d)
TinhS(d);
return s[n];
}
void TinhS(int d) {
int v = 0;
for (int j = 1; j <= k; ++j)
if (d >= b[j]) v += s[d-b[j]];
s[d] = v;
}
Chú ý
Nếu ta sắp tăng mảng b thì chỉ cần duyệt đến khi b[i] > d.
5.16 Trả tiền
Ucraina
Ngân hàng có m loại tiền giấy mệnh giá khác nhau b1 = 1, b2, ..., bk với số lượng không hạn chế. Cần chọn ít nhất là bao nhiêu tở tiền để có tổng bằng t cho trước.
Thí dụ. Có m = 4 loại tiền mệnh giá lần lượt là 1, 2, 7 và 10 quan tiền. Cần trả lại t = 14 quan. Ta chọn 2 tờ tiền mệnh giá 7 quan.
Thuật toán
Gọi s(t) là số tờ tiền ít nhất có tổng bằng t. Nếu trong số này có tờ tiền mệnh giá bi thì tổng số còn lại là tbi và do đó số tờ tiền còn phải trả nốt là s(tbi). Phương án tối ưu khi đó sẽ là
s(t) = min { s(tbi)+1 | i = 1, 2, ... , m; bi t }
Để cài đặt ta dùng mảng s và tính lần lượt các giá trị s[1], s[2], ... , s[t]. Kết quả được hiển thị trên màn hình là s[t].
Độ phức tạp: tm.
Chú ý Điều kiện có loại tiền mệnh giá 1 với số lượng không hạn chế đảm bảo rằng bài toán luôn luôn có nghiệm. Thật vậy, trong trường hợp xấu nhất ta có thể dùng t tờ tiền mệnh giá 1 quan để trả lại.
(* money.pas *)
uses crt;
const fn = 'money.inp'; bl = #32; nl = #13#10;
var b: array[0..31] of integer;
s: array[0..1001] of integer;
m,t: integer;
procedure Doc;
var f: text; i: integer;
begin
assign(f,fn); reset(f);
read(f,m,t);
writeln(nl,m,bl,t);
for i := 1 to m do
begin
read(f,b[i]);
write(b[i],bl);
end;
close(f);
end;
function Min(a,b: integer): integer;
begin if a < b then Min := a else Min := b end;
procedure Tinhs(d: integer);
var i: integer;
begin
s[d] := d;
for i := 2 to m do
if (d >= b[i]) then s[d] := Min(s[d],s[d-b[i]]+1);
end;
function XuLi: integer;
var i: integer;
begin
for i := 1 to t do Tinhs(i);
XuLi := s[t];
end;
BEGIN
Doc;
writeln(nl,XuLi);
writeln(nl,' Fini'); readln;
END.
// DevC++: money.cpp
#include
#include
#include
using namespace std;
// D A T A A N D V A R I A B L E
const char * fn = "money.inp";
int b[31]; // cac loai menh gia
int s[1001];
int m, t;
// P R O T O T Y P E S
void Doc();
int XuLi();
void Tinhs(int);
// I M P L E M E N T A T I O N
int main() {
Doc();
cout << endl << XuLi() << endl;
system("PAUSE");
return EXIT_SUCCESS;
}
void Doc() {
int i;
ifstream f(fn);
f >> m >> t ;
cout << endl << m << " " << t << endl;
for ( i = 1; i < m; ++i) {
f >> b[i];
cout << b[i] << " ";
}
f.close();
}
int XuLi() {
for (int i = 1; i <= t; ++i) Tinhs(i);
return s[t];
}
int Min(int a, int b) {
return (a < b) ? a : b;
}
void Tinhs(int d) {
s[d] = d;
for (int i = 2; i <= m; ++i)
if (d >= b[i]) s[d] = Min(s[d], s[d-b[i]]+1);
}
Chú ý
Nếu ta sắp tăng mảng b thì chỉ cần duyệt đến khi b[i] > d.
Chia sẻ với bạn bè của bạn: |