Tải bản đầy đủ (.pdf) (39 trang)

Đề Cương Ôn Tập Phân Tích Thiết Kế Thuật Toán

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (3.45 MB, 39 trang )

<span class="text_page_counter">Trang 2</span><div class="page_container" data-page="2">

<b>LÝ THUYẾT Định Lý Thợ </b>

Dạng 2: d(n) có dạng log (ko phải hàm nhân) Giả thiết n = b<sup>k</sup> => k = log<small>b</small>n

=> Nghiệm thuần nhất n^(log<small>b</small>a)

Tính nghiệm riêng =

<sup>𝑘−1</sup><sub>𝑗=0</sub>

𝑎

<sup>𝑗</sup>

𝑑(𝑏

<sup>𝑘−𝑗</sup>

)

Tổng dãy số cách đều = (số đầu+số cuối) * số số hạng /2

Xét nghiệm riêng và nghiệm thuần nhất, T(n) = giá trị nghiệm cao nhất

<b>Chia để trị </b>

Áp dụng cho các bài tốn có thể giải quyết bằng cách:  Chia nhỏ ra thành các bài toán con.

 Giải quyết các bài toán con.

 Lời giải của các bài toán con được tổng hợp lại thành lời giải cho bài toán ban đầu.  Các bài toán con sẽ được chia thành các bài toán nhỏ hơn nữa.

 Dừng lại khi kích thước bài tốn đủ nhỏ mà ta có thể giải dễ dàng bài tốn cơ sở.

<b>Quy hoạch động </b>

<i><b>Một số thuật ngữ </b></i>

◦ Bài toán giải theo phương pháp quy hoạch động được gọi là bài toán quy hoa ̣ch động.

</div><span class="text_page_counter">Trang 3</span><div class="page_container" data-page="3">

◦ Cơng thức phối hợp nghiệm của các bài tốn con để có nghiệm của bài tốn lớn gọi là cơng thức truy hồi của quy hoạch động.

◦ Tập các bài tốn nhỏ nhất có ngay lời giải để từ đó giải quyết các bài tốn lớn hơn gọi là cơ sở của quy hoa ̣ch động.

◦ Không gian lưu trữ lời giải các bài tốn con để tìm cách phối hợp chúng gọi là bảng quy hoạch hay bảng phương án của quy hoạch động.

<small>◦ Tính chất thứ nhất và thứ hai là điều kiện cần của một bài tốn quy hoạch động. </small>

<small>◦ Tính chất thứ ba nêu lên đặc điểm của một bài toán mà cách giải bằng phương pháp quy hoạch động hiệu quả hơn hẳn so với phương pháp giải đệ quy thông thường. </small>

<small>◦ Khác với thuật toán đệ quy, phương pháp quy hoạch động thêm vào cơ chế lưu trữ nghiệm hay một phần nghiệm của mỗi bài toán khi giải xong nhằm mục đích sử dụng lại, hạn chế những thao tác thừa trong q trình tính tốn. </small>

<i><b>Mục đích: Cải tiến thuật toán chia để trị hoặc quay lui vét cạn để giảm thời gian thực hiện. Ý tưởng: Lưu trữ các kết quả của các bài toán con trong bảng quy hoạch. Đổi bộ nhớ lấy </b></i>

 Thử các lựa chọn còn lại tại bước này

 Phù hợp với bài toán liệt kê cấu hình dạng X[1, ..., n].

</div><span class="text_page_counter">Trang 5</span><div class="page_container" data-page="5">

<b>Tham Lam </b>

<b>1. Cái ba lô </b>

</div><span class="text_page_counter">Trang 7</span><div class="page_container" data-page="7">

<b>2. Lập lịch </b>

</div><span class="text_page_counter">Trang 8</span><div class="page_container" data-page="8">

<b>3. Sắp xếp môn học </b>

Đoạn code trên giải quyết bài toán lựa chọn các khóa học sao cho số lượng khóa học được chọn là lớn nhất, mà khơng có hai khóa học nào trùng thời gian. Ý tưởng chính của phương pháp Greedy trong đoạn code này là sắp xếp các khóa học theo thời gian kết thúc tăng dần, sau đó lựa chọn lần lượt các khóa học khơng trùng thời gian với nhau.

Giả sử có 5 khóa học như sau:

Khóa học 1 (id=1): bắt đầu lúc 6, kết thúc lúc 7 Khóa học 2 (id=2): bắt đầu lúc 7, kết thúc lúc 9 Khóa học 3 (id=3): bắt đầu lúc 8, kết thúc lúc 14 Khóa học 4 (id=4): bắt đầu lúc 10, kết thúc lúc 20 Khóa học 5 (id=5): bắt đầu lúc 9, kết thúc lúc 12

Sử dụng phương pháp Greedy, ta sẽ sắp xếp các khóa học theo thời gian kết thúc tăng dần.

</div><span class="text_page_counter">Trang 9</span><div class="page_container" data-page="9">

Sau khi sắp xếp, ta lựa chọn lần lượt các khóa học khơng trùng thời gian với nhau. Trong ví dụ này, ta sẽ lựa chọn các khóa học sau: 1, 2, 5, 4. Do đó, số lượng khóa học được chọn là 4 và đây là lịch trình tối ưu.

<b>4. Đóng thùng </b>

Ý tưởng chính của thuật tốn là sắp xếp các đồ vật theo thứ tự giảm dần của kích thước, sau đó lần lượt đặt từng đồ vật vào thùng có thể chứa được nó. Nếu không thể đặt vào thùng hiện tại, thì tìm thùng trống khác có thể chứa được và đặt đồ vật vào đó.

</div><span class="text_page_counter">Trang 10</span><div class="page_container" data-page="10">

Giả sử có các đồ vật có kích thước là {3, 2, 5, 1, 4, 3} và kích thước của thùng là 6. Chương trình sẽ thực hiện như sau:

Sau khi sắp xếp: {5, 4, 3, 3, 2, 1} Lần lượt đặt các đồ vật vào thùng: Thùng 1: {5}, cịn lại kích thước 1 Thùng 2: {4, 1}, cịn lại kích thước 1 Thùng 3: {3, 3}, cịn lại kích thước 0 Thùng 4: {2}, cịn lại kích thước 4

</div><span class="text_page_counter">Trang 11</span><div class="page_container" data-page="11">

<b>5. Rút tiền </b>

Sắp xếp các mệnh giá tiền giảm dần: Trước tiên, danh sách các mệnh giá tiền được sắp xếp theo thứ tự giảm dần, từ mệnh giá lớn nhất đến nhỏ nhất. Điều này giúp chương trình rút tiền bằng các tờ tiền có mệnh giá lớn trước, giảm thiểu số lượng tờ tiền cần rút.

Rút tiền từ mệnh giá lớn nhất đến nhỏ nhất: Thuật toán lặp qua từng mệnh giá tiền từ lớn nhất đến nhỏ nhất. Với mỗi mệnh giá, nó tính số tờ tiền cần rút bằng cách chia số tiền cần rút cho mệnh giá đó và lấy phần nguyên của kết quả. Sau đó, cập nhật số tiền cần rút cho lần lặp tiếp theo.

Giả sử danh sách các mệnh giá tiền có sẵn là {100,000, 50,000, 40,000, 20,000, 10,000}, và người dùng muốn rút 180,000 VND từ máy ATM.

</div><span class="text_page_counter">Trang 12</span><div class="page_container" data-page="12">

Bắt đầu với mệnh giá 100,000 VND:

Số tờ tiền cần rút = 180,000 / 100,000 = 1 (phần nguyên) Cập nhật số tiền cần rút: 180,000 - 1 * 100,000 = 80,000 VND Tiếp tục với mệnh giá 50,000 VND:

Số tờ tiền cần rút = 80,000 / 50,000 = 1 (phần nguyên)

Cập nhật số tiền cần rút: 80,000 - 1 * 50,000 = 30,000 VND ….

</div><span class="text_page_counter">Trang 13</span><div class="page_container" data-page="13">

<b>6. Trồng hoa </b>

</div><span class="text_page_counter">Trang 15</span><div class="page_container" data-page="15">

<b> Bước 3: Kiểm tra xem vị trí đó có phù hợp để đặt qn hậu không bằng cách kiểm tra </b>

các quân hậu đã đặt trước đó. Nếu vị trí đó hợp lệ, tiến hành đặt quân hậu và gọi đệ quy để thử vị trí tiếp theo.

<b> Bước 4: Nếu đã đặt quân hậu vào vị trí cuối cùng trên bàn cờ (i == n), thì in ra cách xếp </b>

hậu đó.

<b> Bước 5: Lặp lại các bước trên cho tất cả các quân hậu cho đến khi tìm được tất cả các </b>

cách xếp hậu đúng trên bàn cờ hoặc khơng cịn cách nào thỏa mãn.

</div><span class="text_page_counter">Trang 17</span><div class="page_container" data-page="17">

<b>2. Dãy nhị phân </b>

Thuật toán quay lui được sử dụng để sinh ra tất cả các tổ hợp nhị phân có độ dài n. Ý tưởng chính của thuật toán là sử dụng một mảng để lưu trữ các giá trị 0 và 1, mỗi giá trị tại một vị trí của mảng biểu diễn cho một bit trong tổ hợp nhị phân. Thuật toán sẽ thử tất cả các kết hợp có thể của các bit này, từ bit đầu tiên đến bit cuối cùng, và đệ quy tiến hành thử các giá trị có thể của bit tiếp theo cho đến khi đạt tới bit cuối cùng. Khi đạt đến bit cuối cùng, thuật toán sẽ in ra kết quả và quay lui để thử các giá trị khác cho các bit trước đó.

Dưới đây là các bước cụ thể của thuật toán:

- Khởi tạo một mảng có độ dài n để lưu trữ các giá trị của tổ hợp nhị phân.

- Gọi hàm quay lui với các tham số là mảng đã khởi tạo, chỉ số của bit hiện tại (bắt đầu từ 1), và độ dài của tổ hợp nhị phân.

- Trong hàm quay lui:

- Lặp qua các giá trị có thể của bit hiện tại (0 hoặc 1). - Gán giá trị cho bit hiện tại.

- Nếu đã đạt tới bit cuối cùng, in ra tổ hợp nhị phân và kết thúc.

- Nếu chưa đạt tới bit cuối cùng, gọi đệ quy để thử các giá trị cho bit tiếp theo.

- Khi đã thử hết tất cả các giá trị cho bit hiện tại, quay lui để thử các giá trị khác cho bit trước đó.

</div><span class="text_page_counter">Trang 18</span><div class="page_container" data-page="18">

<b>Các bước giải: a. Khởi tạo mảng x[] và b[] với giá trị ban đầu. b. Tạo hàm output() để in ra </b>

một hoán vị. c. Tạo hàm đệ quy hoanvi() để sinh ra tất cả các hốn vị:

- Với mỗi vị trí i từ 1 đến n, thử tất cả các số chưa được chọn để điền vào vị trí i. - Đánh dấu số đã chọn và gọi đệ quy để điền số tiếp theo.

- Khi đã điền đủ n số, in ra hốn vị đó.

- Hủy đánh dấu số đã chọn để thử các số khác. d. Gọi hàm hoanvi() từ main() để bắt đầu

<b>sinh và in ra tất cả các hoán vị của các số từ 1 đến n. </b>

</div><span class="text_page_counter">Trang 19</span><div class="page_container" data-page="19">

<b>4. Mã đi tuần </b>

</div><span class="text_page_counter">Trang 20</span><div class="page_container" data-page="20">

<b>5. Mã đi tuần theo chu trình đóng Ý tưởng: </b>

- Sử dụng một ma trận A[][] để lưu vị trí các bước di chuyển trên bàn cờ. - Sử dụng hai mảng X[] và Y[] để định nghĩa các hướng di chuyển từ mỗi ô. - Sử dụng biến dem để đếm số bước đã đi.

- Sử dụng biến x_first và y_first để lưu vị trí ban đầu.

- Sử dụng hàm xuat() để in ra bàn cờ sau mỗi bước di chuyển.

- Sử dụng hàm diChuyen() để thực hiện việc di chuyển từ vị trí hiện tại đến các vị trí khác trên bàn cờ, đồng thời kiểm tra xem mã đã đi hết các ơ chưa và đã trở về vị trí ban đầu chưa.

</div><span class="text_page_counter">Trang 21</span><div class="page_container" data-page="21">

<b>6. Tổng các tập con </b>

</div><span class="text_page_counter">Trang 22</span><div class="page_container" data-page="22">

<b>7. Sudoku </b>

<b>- Kiểm tra tính hợp lệ của giá trị trong ơ: kiểm tra xem có thể đặt giá trị k vào vị trí (x, </b>

y) trên bảng Sudoku hay khơng. Kiểm tra xem giá trị k đã tồn tại trong hàng, cột hoặc ơ 3x3 chứa vị trí (x, y) chưa. Nếu không hợp lệ, trả về 0; ngược lại trả về 1.

<b>- Giải bài toán Sudoku bằng đệ quy: Sử dụng phương pháp đệ quy để thử tất cả các giá </b>

trị từ 1 đến 9 cho ô hiện tại và tiếp tục đệ quy để thử giá trị tiếp theo cho ơ kế tiếp. Nếu khơng tìm thấy giải pháp, quay lui và thử các giá trị khác cho ơ hiện tại.

<b>- Tìm kiếm giải pháp: kết thúc cột hiện tại, kiểm tra xem đã đến cuối bảng chưa. Nếu đã </b>

đến cuối bảng, in ra giải pháp và kết thúc chương trình. Nếu chưa, sẽ tiếp tục đệ quy với hàng tiếp theo. Nếu ô hiện tại đã có giá trị, nó sẽ tiếp tục đệ quy với ô kế tiếp. Nếu ô hiện

<b>tại chưa có giá trị, nó sẽ thử tất cả các giá trị hợp lệ cho ơ đó. </b>

</div><span class="text_page_counter">Trang 23</span><div class="page_container" data-page="23">

<b>8. Tổ hợp chập k của n </b>

</div><span class="text_page_counter">Trang 24</span><div class="page_container" data-page="24">

<b>Quy hoạch động </b>

Ý tưởng chính là sử dụng một bảng hoặc một mảng để lưu trữ kết quả của các bài toán con nhỏ.

 Ta bắt đầu với các trường hợp cơ bản: C(n, 0) = C(n, n) = 1.

 Sau đó, ta sử dụng công thức tổ hợp: C(n, k) = C(n-1, k-1) + C(n-1, k) để tính tốn các giá trị cịn lại.

 Bằng cách tính tốn từ trên xuống dưới và từ trái sang phải, ta có thể xây dựng bảng hoặc mảng chứa kết quả của tất cả các bài toán con nhỏ.

 Kết quả cuối cùng sẽ nằm ở ơ góc phải dưới cùng của bảng hoặc mảng.

</div><span class="text_page_counter">Trang 25</span><div class="page_container" data-page="25">

<b>2. </b>

<b>Dãy Fibonacci</b>

Khởi tạo mảng F và giá trị ban đầu của dãy Fibonacci: Mảng F được khai báo với kích thước n và được sử dụng để lưu trữ các số trong dãy Fibonacci. Ban đầu, hai phần tử đầu tiên của dãy Fibonacci là 0 và 1 được gán trực tiếp vào F[0] và F[1].

Duyệt qua từng phần tử của dãy Fibonacci từ 2 đến n: Sử dụng một vòng lặp, với mỗi giá trị i từ 2 đến n, tính giá trị của F[i] bằng cách cộng hai giá trị trước đó của dãy Fibonacci, tức là F[i-1] và F[i-2].

In ra dãy Fibonacci: Cuối cùng, dãy Fibonacci được in ra màn hình từ F[0] đến F[n].

<b>3. Tổng tập con </b>

</div><span class="text_page_counter">Trang 26</span><div class="page_container" data-page="26">

Sử dụng bảng hoặc mảng: Bước đầu tiên là tạo một bảng hoặc mảng 1 chiều để lưu trữ kết quả của các bài toán con nhỏ. Mỗi phần tử trong mảng này thường là một giá trị boolean, chỉ ra xem có thể tạo thành tổng mục tiêu từ một tập con của dãy ban đầu hay không.

Trường hợp cơ bản: Khởi tạo các giá trị cơ bản của bảng hoặc mảng. Ví dụ, nếu mục tiêu là 0, thì tất cả các giá trị trong mảng này đều là True vì có thể tạo ra tổng 0 từ bất kỳ tập con nào (bằng cách không chọn phần tử nào).

Tính tốn các giá trị còn lại: Sử dụng phương pháp quy hoạch động, tính tốn các giá trị cịn lại của bảng hoặc mảng dựa trên giá trị của các bài tốn con nhỏ hơn. Mỗi ơ của bảng hoặc mảng thường được tính tốn dựa trên hai trường hợp: (a) nếu không bao gồm phần tử hiện tại, (b) nếu bao gồm phần tử hiện tại.

Trả về kết quả: Kết quả cuối cùng thường được lấy từ ô cuối cùng của bảng hoặc mảng. Nếu giá trị tại ô này là True, có nghĩa là có thể tạo thành tổng mục tiêu từ một tập con của dãy ban đầu.

</div><span class="text_page_counter">Trang 27</span><div class="page_container" data-page="27">

<b>4. Cái ba lô </b>

Ý tưởng giải bài toán:

Xây dựng bảng: Tạo một bảng 2 chiều có kích thước n x W, trong đó n là số lượng đồ vật và W là trọng lượng tối đa của cái ba lô.

Khởi tạo giá trị ban đầu: Đặt tất cả các giá trị trong hàng đầu tiên và cột đầu tiên của bảng là 0.

Tính tốn giá trị tối ưu: Sử dụng công thức tối ưu L[i][t] = max(L[i - 1][t], L[i][t-g[i]] + v[i]), trong đó v [i] là giá trị của đồ vật thứ i, g[i] là trọng lượng của đồ vật thứ i, và L[i][t] là giá trị tối ưu có thể đạt được với i đồ vật và trọng lượng t.

Trả về giá trị tối ưu: Giá trị tối ưu cuối cùng sẽ nằm ở ơ góc dưới cùng bên phải của bảng

</div><span class="text_page_counter">Trang 28</span><div class="page_container" data-page="28">

<b>5. Dãy con tăng dài nhất </b>

Ý tưởng giải bài toán:

Khởi tạo mảng L để lưu độ dài của dãy con tăng dài nhất kết thúc tại mỗi vị trí của dãy. Bổ sung phần tử ảo INT_MAX vào cuối dãy A

Duyệt qua từng phần tử của dãy, cập nhật giá trị của L[i] bằng cách so sánh với các phần tử

</div><span class="text_page_counter">Trang 29</span><div class="page_container" data-page="29">

Trả về độ dài của dãy con tăng dài nhất và in ra dãy con đó. Lập bảng quy hoạch

| 3 4 2 8 10 5 1 ∞ --- 1 | 1 1 1 1 1 1 1 1 1 2 2 3 4 3 2 1

</div><span class="text_page_counter">Trang 30</span><div class="page_container" data-page="30">

<b>6. Đổi tiền </b>

Khởi tạo mảng P và L: Mảng P được khởi tạo với giá trị INT_MAX, đại diện cho số lượng đồng tiền ít nhất cần để đổi đến số tiền tương ứng. Mảng L cũng được khởi tạo tương tự nhưng có thêm phần tử 0 ở đầu.

Duyệt qua từng loại đồng tiền và mỗi mức số tiền từ 1 đến M: Tại mỗi bước lặp, ta cập nhật mảng L dựa trên mảng P, sử dụng các loại đồng tiền hiện có.

Cập nhật mảng L: Tại mỗi mức số tiền t, ta cập nhật L[t] bằng giá trị nhỏ nhất giữa P[t] (số lượng đồng tiền ít nhất cho số tiền t mà không sử dụng đồng tiền hiện tại) và L[t-a[i]] + 1 (số lượng đồng tiền ít nhất cho số tiền t - a[i] cộng thêm một đồng tiền hiện tại).

Trả về kết quả: Trả về giá trị cuối cùng của L[M], tức là số lượng đồng tiền ít nhất cần thiết để đổi số tiền M.

Lập bảng quy hoạch

| 0 1 2 3 4 5 6 7 8 9 10 ... --- 0 | 0 ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ... 1 | 0 1 2 3 4 5 6 7 8 9 10 ... 2 | 0 1 1 2 2 3 3 4 4 5 5 ... 3 | 0 1 1 1 2 2 2 3 3 3 4 ... 5 | 0 1 1 1 2 1 2 2 3 2 3 ... 10 | 0 1 1 1 2 1 2 2 3 2 1 ...

</div><span class="text_page_counter">Trang 31</span><div class="page_container" data-page="31">

<b>7. Độ dài xâu con dài nhất </b>

Duyệt qua từng ký tự trong xâu X và Y:

 Nếu X[i] bằng Y[j], tức là ta tìm thấy một ký tự mới trong xâu con chung. Ta cập nhật giá trị của L[j] bằng giá trị của P[j-1]

 Ngược lại, ta cập nhật giá trị của L[j] bằng giá trị lớn nhất giữa P[j] và L[j-1]

Sau khi duyệt qua tất cả các ký tự trong X và Y, ta cập nhật mảng P bằng mảng L và tiếp tục duyệt.

Kết quả cuối cùng là giá trị của L[ny], tức là độ dài của xâu con chung dài nhất. Lập bảng quy hoạch

| C o n g C h i e n --- 0 | 0 0 0 0 0 0 0 0 0 C 0 | 0 1 1 1 1 1 1 1 1 h 0 | 0 1 1 1 1 2 2 2 2 i 0 | 0 1 1 1 1 2 3 3 3

</div><span class="text_page_counter">Trang 32</span><div class="page_container" data-page="32">

e 0 | 0 1 1 1 1 2 3 3 4 n 0 | 0 1 1 1 1 2 3 4 4 C 0 | 0 1 2 2 2 2 2 2 2 o 0 | 0 1 2 2 3 3 3 3 3 n 0 | 0 1 2 3 3 3 3 3 3 g 0 | 0 1 2 3 4 4 4 4 4

2. Kiểm tra phần tử ở giữa:

So sánh phần tử ở giữa mảng với giá trị cần tìm. Nếu phần tử này trùng khớp với giá trị cần tìm, chúng ta đã tìm thấy nó và trả về chỉ số của phần tử đó.

3. Chọn phần mảng để tiếp tục tìm kiếm:

Nếu phần tử ở giữa mảng lớn hơn giá trị cần tìm, chúng ta chỉ cần tìm kiếm trong nửa mảng bên trái.

</div><span class="text_page_counter">Trang 33</span><div class="page_container" data-page="33">

Nếu phần tử ở giữa mảng nhỏ hơn giá trị cần tìm, chúng ta chỉ cần tìm kiếm trong nửa mảng bên phải.

4. Lặp lại quá trình cho đến khi tìm thấy hoặc khơng thể tìm thấy:

Lặp lại quá trình trên cho đến khi tìm thấy phần tử cần tìm hoặc phần tử này khơng tồn tại trong mảng. Nếu phần tử không tồn tại, chúng ta sẽ trả về một giá trị đặc biệt để biểu thị việc khơng tìm thấy.

5. Trả về kết quả:

Nếu tìm thấy phần tử, trả về chỉ số của nó trong mảng.

Nếu khơng tìm thấy, trả về một giá trị đặc biệt để biểu thị việc không tìm thấy.

</div><span class="text_page_counter">Trang 34</span><div class="page_container" data-page="34">

<b>2. </b>

<b>Dãy con liên tục có tổng lớn nhất </b>

1. Khởi tạo các biến:

max_so_far: lưu trữ tổng lớn nhất của dãy con liên tục đã tìm thấy. curr_max: lưu trữ tổng tạm thời của dãy con liên tục hiện tại. begin và end: lưu trữ chỉ số của dãy con liên tục có tổng lớn nhất. 2. Duyệt qua mảng:

Bắt đầu từ phần tử thứ 2 của mảng, vì chúng ta đã xử lý phần tử đầu tiên ở bước khởi tạo. Duyệt qua từng phần tử của mảng:

So sánh a[i] với curr_max + a[i]. Nếu a[i] lớn hơn tổng tạm thời hiện tại (curr_max + a[i]), tức là bắt đầu một dãy con mới từ a[i].

Cập nhật curr_max thành giá trị lớn hơn giữa a[i] và curr_max + a[i]. So sánh max_so_far với curr_max để cập nhật giá trị max_so_far nếu cần. 3. Xác định dãy con liên tục có tổng lớn nhất:

Sau khi duyệt qua tồn bộ mảng, max_so_far sẽ chứa tổng lớn nhất của dãy con liên tục. Sử dụng begin và end để in ra dãy con liên tục có tổng lớn nhất.

4. Trả về kết quả:

Trả về giá trị max_so_far, tức là tổng lớn nhất của dãy con liên tục.

</div>

×