Hàm f(n)

Một phần của tài liệu Sáng tạo trong thuật toán và lập trình với ngôn ngữ Pascal và C# Tập 3 - Chương 5 pps (Trang 41 - 46)

/ i goi tin chi chuyen tren kenh 1 voi chi phi c[i][1] p[0] = 0; chuyen 0 goi tin tren kenh

25 là số sát sau số 22 chứa đúng 3 bi t1 trong dạng nhị phân.

5.12 Hàm f(n)

Tính gía trị của hàm f(n) với biến số nguyên n cho trước, 0 n 1.000.000.000 (1 tỷ). Biết f(0) = 0; f(1) = 1; f(2n) = f(n); f(2n+1) = f(n) + f(n+1).

Thuật toán

Xét hàm 3 biến g(n,a,b) = af(n) + bf(n+1). Ta có, 1) g(n,1,0) = 1.f(n) + 0.f(n+1) = f(n).

2) g(0,a,b) = af(0) + bf(1) = a.0 + b.1 = b.

3) g(2n,a,b) = af(2n) + bf(2n+1) = af(n) + bf(n) + bf(n+1) = (a+b)f(n) + bf(n+1) = g(n,a+b,b).

4) g(2n+1,a,b) = af(2n+1)+bf(2n+2) = af(n) + af(n+1) + bf(2(n+1)) = af(n) + af(n+1) + bf(n+1) = af(n) + (a+b)f(n+1) = g(n,a,a+b).

Từ bốn tính chất trên ta thiết kế được hàm f(n) như sau:

Để tính f(n) ta tính g(n,a,b) với a = 1, b = 0. Để tính g(n) ta lặp đến khi n = 0. Nếu n chẵn ta gọi hàm

g(n/2,a+b,b); ngược lại, nếu n lẻ ta gọi hàm g(n/2,a,a+b). Khi n = 0 ta thu được f = g(0,a,b) = b.

(* Pascal *)

function f(n: longint): longint; var a,b: longint;

begin

a := 1; b := 0; while (n <> 0) do while (n <> 0) do begin

if odd(n) then b := b + a else a := a + b; n := n div 2;

end; f := b; end;

int f(int n) { int a = 1, b = 0; while (n) { if (n % 2 == 0) a += b; else b += a; n /= 2; } return b; } Độ phức tạp log2(n) vòng lặp. Dữ liệu test f(100) = 7; f(101) = 19; f(1000000) = 191; f(1000000000) = 7623. 5.13 Hàm h(n)

Tính hàm h(n) với giá trị n cho trước 0 n 1.000.000 (1 triệu). Biết h(0) = 3; h(1) = 1; h(2n) = 2h(n); h(2n+1) = h(n) – 2h(n+1).

Thuật toán

Xét hàm 3 biến g(n,a,b) = ah(n) + bh(n+1). Ta có, 1) g(n,1,0) = h(n). 2) g(0,a,b) = 3a + b. 3) g(2n,a,b) = g(n,2a+b,–2b). 4) g(2n+1,a,b) = g(n,a,2b–2a). Dữ liệu test h(100) = 176; h(101) = 128; h(1000000) = 3162112; h(1000001) = 1933312. 5.14 Rhythm

Viết hàm Rhythm(s) cho ra dáng điệu của xâu kí tự s = (s1. s2,…, sn) như sau

Rhythm(s) = 1, nếu các kí tự trong s đều bằng nhau: s1= s2=…= sn,

Rhythm(s) = 2, nếu các kí tự trong s tạo thành dãy tăng chặt: s1 < s2 <…< sn,

Rhythm(s) = 3, nếu các kí tự trong s tạo thành dãy đồng biến: s1 s2 sn,

Rhythm(s) = 4, nếu các kí tự trong s tạo thành dãy giảm chặt: s1 > s2 >… > sn,

Rhythm(s) = 5, nếu các kí tự trong s tạo thành dãy nghịch biến: s1 s2 sn,

Rhythm(s) = 0, nếu không xảy ra các tình huống trên,

Kết quả được chọn ưu tiên cho các giá trị nhỏ. Thí dụ, Rhythm("'aaaaa") = 1, trong khi các tình huống 3 và 5 cũng thỏa. Biết 2 n 255.

Ta sử dụng 3 biến đếm b (bằng), t (tăng), g (giảm) và duyệt tuần tự xâu s để ghi nhận các tình huống khi di chuyển từ phần tử si sang phần tử tiếp theo si+1. Cụ thể là ta gán

b = 1 nếu si = si+1, t = 1 nếu si < si+1, g = 1 nếu si > si+1.

Giá trị của hàm Rhythm chính là số nguyên có dạng biểu diến nhị phân (g,t,b). Cụ thể là với 5 trường hợp đầu ta có

Rhythm = 4*g + 2*t + b

Hai trường hợp cuối ứng với các trị 6 và 7 được chuyển về 0. Tóm lại, ta có

Rhythm = ((4*g + 2*t + b) mod 7) mod 6

g t b Rhythm 0 0 1 1 0 1 0 2 0 1 1 3 1 0 0 4 1 0 1 5 1 1 0 6 0 1 1 1 7 0 Độ phức tạp. cỡ n. (* Rhythm.pas *) uses crt;

function Rhythm(s: string): integer; var i, g, t, b: integer;

begin

g := 0; t := 0; b := 0; for i := 2 to length(s) do for i := 2 to length(s) do if s[i] = s[i-1] then b := 1

else if s[i] > s[i-1] then t := 1 else { s[i] < s[i-1] } g := 1;

Rhythm := ((4*g + 2*t + b) mod 7 mod 6); end; BEGIN writeln(Rhythm('aabccdx')); { 3 } readln; END. // Rhythm.cpp #include <string.h> #include <fstream> #include <iostream> #include <stdio.h> using namespace std; // P R O T O T Y P E S int Rhythm(char *); // I M P L E M E N T A T I O N int main() {

cout << endl << Rhythm("aabccdxxxz") << endl; // 3 cout << endl << " Fini ";

cin.get(); return 0; } int Rhythm(char * s) { int g, t, b, i, n; g = t = b = 0; n = strlen(s); for (i = 1; i < n; ++i) if (s[i] == s[i-1]) b = 1;

else if (s[i] > s[i-1]) t = 1; else g = 1;

return ((4*g + 2*t + b) % 7) % 6; }

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à nb1, do đó tổng số phương án của nhóm này sẽ là S(nb1).

 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à nb2, do đó tổng số phương án của nhóm này sẽ là S(nb2).

 ...

 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à nbi, do đó tổng số phương án của nhóm này sẽ là S(nbi).

 ...

 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à nbk, do đó tổng số phương án của nhóm này sẽ là S(nbk).

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(nbi) | 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 <string.h> #include <fstream> #include <iostream> 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à tbi và do đó số tờ tiền còn phải trả nốt là s(tbi). Phương án tối ưu khi đó sẽ là

s(t) = min { s(tbi)+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;

Một phần của tài liệu Sáng tạo trong thuật toán và lập trình với ngôn ngữ Pascal và C# Tập 3 - Chương 5 pps (Trang 41 - 46)

Tải bản đầy đủ (PDF)

(54 trang)