Tuy nhiên trong quá trình giảng dạy nâng cao đại đa số giáoviên chú trọng đến việc giảng dạy kiến thức nền tảng giải thuật rất ít, giáo viên chỉ dạy lướt qua rồi cho học sinh làm bài để
TÊN SÁNG KIẾN, LĨNH VỰC ÁP DỤNG
- Tên sáng kiến: “Định hướng giảng dạy giải thuật và lập trình về quay lui và quy hoạch động cơ bản”
- Lĩnh vực áp dụng: Giảng dạy HSG bộ môn tin học THPT
NỘI DUNG SÁNG KIẾN
Giải pháp cũ thường làm
Khi dạy HSG giáo viên thường sẽ phát triển kiến thức, kỹ năng của học sinh thông qua các bước như:
- Xác định mức độ kiến thức của HS
- Củng cố, rèn luyện lại kiến thức, kỹ năng cơ bản xác định bài toán cho học sinh
- Giải các bài toán cơ bản làm nền tảng, xây dựng kỹ năng làm bài cho học sinh
Trong quá trình giảng dạy, có những bước giáo viên chỉ mất ít thời gian, nhưng cũng có những bước tốn nhiều thời gian hơn, đặc biệt là bước dạy nâng cao Bước này rất quan trọng vì nó giúp phát triển tư duy và áp dụng kiến thức cơ bản vào các bài toán khó Đây là yếu tố quyết định khả năng tiếp thu của học sinh và ảnh hưởng đến kết quả cao trong kỳ thi chọn HSG.
Trong quá trình giảng dạy nâng cao, nhiều giáo viên thường chỉ tập trung vào việc rèn luyện kỹ năng cho học sinh mà ít chú trọng đến việc dạy kiến thức nền tảng về giải thuật Họ thường lướt qua các khái niệm cơ bản và yêu cầu học sinh làm bài tập để áp dụng kiến thức, mà không nhận ra rằng nhiều bài toán có thể được giải quyết dễ dàng hơn bằng các thuật toán cơ bản.
Sau nhiều năm giảng dạy HSG bằng phương pháp thực nghiệm tại trường THPT Gia Viễn B, chúng tôi nhận thấy rằng các bước dạy HSG vẫn còn một số điểm chưa đáp ứng được yêu cầu của Ban Hướng dẫn, tổ và nhóm chuyên môn đề ra.
Việc dạy học sinh giỏi (HSG) thường dựa vào kinh nghiệm lâu năm của các giáo viên dày dạn, trong khi những giáo viên mới hoặc ít kinh nghiệm thường kết hợp giữa việc giảng dạy và tìm hiểu thêm để nâng cao kỹ năng.
Thứ hai: Việc dạy HSG như thế làm giáo viên “lười” tìm hiểu kiến thức, kỹ năng rèn luyện bản thân, trau dồi kiến thức cho minh.
Học sinh thường thiếu hiểu biết sâu sắc về lập trình, dẫn đến việc họ giải quyết bài toán ngay lập tức mà không dành thời gian để tư duy và suy nghĩ Cần tìm kiếm các phương án tối ưu để giải quyết vấn đề một cách hiệu quả hơn.
Kết quả dạy HSG vào thứ tư không đạt hiệu quả cao, dẫn đến việc không tạo động lực cho học sinh lớp sau yêu thích và đam mê môn tin học cũng như lập trình Điều này gây ra cảm giác "sợ hãi" cho những học sinh khác.
Thứ năm: Không có một phương án thống nhất, một hệ thống kiến thức, bài tập xuyên suốt để giáo viên dạy HSG.
Giải pháp mới cải tiến
Sau nhiều năm giảng dạy học sinh giỏi (HSG), chúng tôi nhận thấy rằng việc giải quyết vấn đề này đòi hỏi một hệ thống kiến thức và bài tập cơ bản về dạy học nâng cao cho HSG Trong hệ thống này, hai phần quan trọng nhất mà tất cả các đề thi HSG môn tin học của huyện và tỉnh đều có là giải thuật và lập trình liên quan đến quay lui và quy hoạch động.
Do đó chúng tôi làm sáng kiến kinh nghiệm có tên: “Giải thuật lập trình về quay lui và quy hoạch động cơ bản”.
HIỆU QUẢ KINH TẾ VÀ XÃ HỘI
Hiệu quả kinh tế
Việc biên soạn một chủ đề dạy học đòi hỏi giáo viên phải dành nhiều thời gian để tìm kiếm và biên tập tài liệu từ internet và sách tham khảo Học sinh cũng phải tốn thời gian để tìm kiếm bài tập luyện tập và hệ thống hóa chúng Thay vì mất thời gian cho những công việc này, giáo viên và học sinh có thể sử dụng tài liệu có sẵn để học tập và ôn thi học sinh giỏi Nếu cần thiết, giáo viên chỉ cần bổ sung hàng năm để tạo ra một kho tài liệu phong phú về bài tập cho riêng mình.
Xét về khía cạnh tài chính, việc tạo ra tài liệu này giúp giáo viên tiết kiệm thời gian và chi phí Thay vì phải dành nhiều giờ tìm kiếm thông tin và sáng tạo bài toán trên internet, giáo viên chỉ cần phô tô hoặc in tài liệu với chi phí không quá 10.000 đồng Điều này không chỉ mang lại hiệu quả kinh tế cao mà còn cho phép tài liệu được sử dụng trong nhiều năm tiếp theo.
Hiệu quả xã hội
Sáng kiến này không chỉ cải thiện phương pháp dạy học sinh giỏi theo hướng tích cực và chủ động, mà còn cung cấp cho giáo viên và học sinh một nền tảng kiến thức vững chắc về các dạng bài tập quay lui và quy hoạch động thường gặp Điều này giúp nâng cao kỹ năng cho giáo viên và chất lượng đội tuyển học sinh giỏi hàng năm, đồng thời tạo ra một môi trường thuận lợi cho việc phát triển các đội tuyển học sinh giỏi tiếp theo trong môn tin học và cho nhà trường.
Sáng kiến này không chỉ là tài liệu tham khảo hữu ích cho giáo viên và học sinh yêu thích lập trình, mà còn hỗ trợ hiệu quả trong quá trình ôn thi học sinh giỏi.
ĐIỀU KIỆN VÀ KHẢ NĂNG ÁP DỤNG
Mỗi nhà trường cần thiết lập một kế hoạch dạy học hợp lý, phù hợp với đặc điểm và nhu cầu của học sinh tại trường để áp dụng sáng kiến này hiệu quả.
Tài liệu này đã được thực nghiệm và áp dụng hiệu quả trong các năm học trước, mang lại lợi ích lớn cho công tác giảng dạy của giáo viên Nó không chỉ nâng cao chất lượng giảng dạy và học tập môn tin học 11 mà còn tạo hứng thú học tập và phát triển năng lực tự học cho học sinh Do đó, sáng kiến này có thể được áp dụng rộng rãi trong các trường học và toàn tỉnh.
Chúng tôi xin cam đoan mọi thông tin nêu trong đơn là trung thực, đúng sự thật và hoàn toàn chịu trách nhiệm trước pháp luật.
XÁC NHẬN CỦA LÃNH ĐẠO ĐƠN VỊ CƠ SỞ
Gia Viễn, ngày 11 tháng 05 năm
(Ký và ghi rõ họ tên)
GIẢI QUYẾT VẤN ĐỀ
CƠ SỞ LÝ LUẬN
Quay lui là một trong những kỹ thuật quan trọng vì nó cho phép giải một lớp các bài toán khá lớn có dạng tổng quát như:
- Tìm một (hoặc tất cả) bộ nghiệm (x1, x2,…, xn) thỏa mãn điều kiện F nào đó, trong đó các thành phần xi được chọn từ tập hữu hạn
Tư tưởng của phương pháp quay lui như sau:
- Ta xây dựng bộ nghiệm từng bước, bắt đầu từ thành phần nghiệm x1 được chọn trong các giá trị có thể S1 = D1.
Giả sử đã chọn các thành phần x1, x2, …, xi-1, ta xác định tập Si gồm các giá trị cho thành phần nghiệm xi, với Si là tập con của Di và phụ thuộc vào các thành phần đã chọn Chọn một phần tử xi từ Si làm thành phần của bộ nghiệm Tiếp tục lặp lại quá trình này để mở rộng nghiệm cho xi+1 Nếu không thể chọn được phần tử nào cho xi+1 (do Si+1 rỗng), ta sẽ quay lại và chọn một phần tử khác từ Si làm thành phần nghiệm xi, thể hiện ý nghĩa của phương pháp quay lui.
Trong quá trình mở rộng nghiệm, việc kiểm tra xem nghiệm thành phần có phải là nghiệm của bài toán hay không là rất quan trọng Nếu chỉ cần một nghiệm, ta sẽ dừng lại; ngược lại, nếu cần tìm tất cả các nghiệm, quá trình tìm kiếm chỉ kết thúc khi tất cả các khả năng chọn các thành phần nghiệm đã được thỏa mãn.
Quay lui là một phương pháp hiệu quả trong việc chọn giá trị nghiệm mà không cần duyệt tuyến tính Thuật toán quay lui có ưu điểm so với phương pháp tuyến tính ở chỗ nó hạn chế các nhánh không cần thiết, giúp tiết kiệm thời gian tìm kiếm lời giải Cụ thể, thuật toán này xây dựng các tập giá trị khả thi Si khi tìm kiếm thành phần nghiệm xi và sẽ quay lui khi không thể mở rộng thành phần nghiệm xi+1.
Phương pháp duyệt qua nhiều khả năng có độ phức tạp cao, thường ở mức giai thừa hoặc hàm mũ, dẫn đến tốc độ tính toán chậm khi kích thước dữ liệu lớn Để khắc phục, người ta áp dụng phương pháp nhánh cận để hạn chế các khả năng không mang lại kết quả, mặc dù không phải bài toán nào cũng phù hợp với phương pháp này Hầu hết các bài toán sử dụng phương pháp duyệt đệ quy để kiểm tra mọi khả năng có thể có nghiệm theo nguyên tắc “thử sai” và quay lui Mặc dù các thuật toán này đơn giản về tư tưởng, việc áp dụng để giải quyết các bài toán đòi hỏi kỹ thuật nhất định Để giải bài toán bằng thuật toán quay lui, cần thực hiện một số công việc quan trọng.
- Tìm cách biểu diễn nghiệm của bài toán dưới dạng một dãy các đối tượng được chọn dần từng bước (x1, x2, …, xn).
- Xác định được tập Si các khả năng được chọn làm thành phần thứ i của nghiệm Chọn cách thích hợp để biểu diễn Si.
- Tìm các điều kiện để các nghiệm đã chọn là các nghiệm của bài toán.
Một số lưu ý khi xây dựng thuật toán quay lui:
- Phải duyệt qua mọi phương án của bài toán có thể chứa nghiệm.
- Tránh trường hợp duyệt trùng lặp các khả năng đã duyệt.
Thông thường ta thường dùng thủ tục đệ quy Try(i : Integer) để chọn thành phần nghiệm xi.
Có ba dạng cơ bản trong các thuật toán quay lui:
Dạng 1: Tìm tất cả các nghiệm.
Dạng 3: Tìm nghiệm tối ưu thỏa mãn điều kiện.
Trong sáng kiến kinh nghiệm này ta lần lượt xét cách giải các dạng trên bằng phương pháp quay lui.
2 Phương pháp quy hoạch động
Quy hoạch động là phương pháp giải bài toán tối ưu có tính chất đệ quy, cho phép tìm ra phương án tối ưu bằng cách chia bài toán lớn thành các bài toán con hữu hạn Nguyên lý chia để trị thường được áp dụng trong thiết kế thuật toán, giúp giải quyết các bài toán con độc lập Trong quy hoạch động, khi chưa xác định được các bài toán con cần giải, ta sẽ giải quyết toàn bộ và lưu trữ các lời giải để tái sử dụng, từ đó hỗ trợ giải quyết các bài toán tổng quát hơn Sự khác biệt giữa quy hoạch động và phân giải đệ quy nằm ở cách tiếp cận và lưu trữ kết quả.
Phép phân giải đệ quy là phương pháp bắt đầu từ một bài toán lớn, sau đó chia nhỏ thành nhiều bài toán con để giải quyết Quá trình này tiếp tục phân chia các bài toán con thành những bài toán nhỏ hơn, và giải quyết từng bài toán nhỏ, bất kể chúng đã được giải hay chưa.
Quy hoạch động bắt đầu bằng việc giải quyết các bài toán cơ sở nhỏ nhất, từ đó từng bước tiến tới giải quyết các bài toán lớn hơn, cho đến khi đạt được giải pháp cho bài toán lớn nhất, tức là bài toán ban đầu.
Trước khi áp dụng phương pháp quy hoạch động ta phải xét xem phương pháp đó có thỏa mãn những yêu cầu dưới đây không:
Bài toán lớn cần được chia nhỏ thành nhiều bài toán con, và việc phối hợp các lời giải của những bài toán con này sẽ giúp chúng ta tìm ra lời giải cho bài toán lớn.
Phương pháp quy hoạch động yêu cầu đủ không gian vật lý để lưu trữ các lời giải của các bài toán con Nếu không có đủ bộ nhớ hoặc dung lượng đĩa, việc áp dụng quy hoạch động sẽ không khả thi.
Quá trình từ bài toán cơ sở tìm ra lời giải bài toán ban đầu phải qua hữu hạn bước
Các bước cài đặt một chương trình sử dụng quy hoạch động:
- Giải tất cả các bài toán cơ sở (thông thường rất dễ), lưu các lời giải vào bảng phương án.
Sử dụng công thức truy hồi để kết hợp các lời giải từ những bài toán nhỏ đã được lưu trữ trong bảng phương án, nhằm tìm ra lời giải cho các bài toán lớn hơn Quá trình này tiếp tục cho đến khi tìm được lời giải cho bài toán ban đầu và lưu trữ kết quả vào bảng phương án.
- Dựa vào bảng phương án, truy vết tìm ra nghiệm tối ưu
Cho đến nay, chưa có định lý nào xác định rõ ràng những bài toán nào có thể giải quyết hiệu quả bằng quy hoạch động Để xác định khả năng áp dụng quy hoạch động cho một bài toán, ta có thể đặt ra hai câu hỏi: “Liệu một nghiệm tối ưu của bài toán lớn có thể được hình thành từ sự kết hợp của các nghiệm tối ưu của các bài toán con không?” và “Có thể lưu trữ nghiệm của các bài toán con dưới một hình thức nào đó để hỗ trợ tìm kiếm nghiệm cho bài toán lớn không?”.
CƠ SỞ THỰC TIỄN
Gần 50% bài thi trong các cuộc thi lập trình yêu cầu sử dụng quy hoạch động và quay lui Mặc dù có nhiều phương pháp khác để giải quyết vấn đề, nhưng do thời gian hạn chế trong các cuộc thi, việc áp dụng các kỹ thuật này trở nên cần thiết.
Trong các bài toán lập trình, thời gian giới hạn cho mỗi phép thử thường chỉ 1 giây, cùng với giới hạn về bộ nhớ của chương trình, do đó, việc sử dụng một thuật toán hiệu quả là rất quan trọng Trong những tình huống này, quy hoạch động và thuật toán quay lui vét can là những phương pháp phổ biến nhất.
Thuật toán quy hoạch động và quay lui rất được ưa chuộng do tính đa dạng của các bài toán, đòi hỏi người giải phải tư duy sâu sắc để tìm ra lời giải Không có công thức chung nào áp dụng cho tất cả các bài toán, vì vậy học sinh cần phải thành thạo hai thuật toán này để đạt kết quả tốt trong các cuộc thi.
CÁC BIỆN PHÁP ĐỂ GIẢI QUYẾT VẤN ĐỀ
Thuật toán quay lui là phương pháp hiệu quả để liệt kê các cấu hình, trong đó mỗi cấu hình được hình thành bằng cách xây dựng từng phần tử Mỗi phần tử được chọn thông qua việc thử nghiệm tất cả các khả năng có thể.
Giả sử cấu hình cần liệt kê có dạng x[1 n], khi đó thuật toán quay lui thực hiện qua các bước:
1) Xét tất cả các giá trị x[1] có thể nhận, thử cho x[1] nhận lần lượt các giá trị đó Với mỗi giá trị thử gán cho x[1] ta sẽ:
2) Xét tất cả các giá trị x[2] có thể nhận, lại thử cho x[2] nhận lần lượt các giá trị đó Với mỗi giá trị thử gán cho x[2] lại xét tiếp các khả năng chọn x[3] … cứ tiếp tục như vậy đến bước:
… n) Xét tất cả các giá trị x[n] có thể nhận, thử cho x[n] nhận lần lượt các giá trị đó, thông báo cấu hình tìm được 〈x[1], x[2], …, x[n]〉.
Thuật toán quay lui liệt kê các cấu hình n phần tử x[1 n] bằng cách thử nghiệm từng giá trị có thể cho x[1] Mỗi khi gán một giá trị cho x[1], bài toán sẽ tiếp tục liệt kê các cấu hình của n - 1 phần tử x[2 n].
The procedure attempts to assign all possible values to the variable \( x[i] \) It iterates through each potential value \( V \) that can be assigned to \( x[i] \).
〈Thử cho x[i] := V〉; if 〈x[i] là phần tử cuối cùng trong cấu hình〉 then
〈Thông báo cấu hình tìm được〉 else begin
〈Ghi nhận việc cho x[i] nhận giá trị V (nếu cần)〉;
Try(i + 1); {Gọi đệ quy để chọn tiếp x[i+1]}
〈Nếu cần, bỏ ghi nhận việc thử x[i] := V để thử giá trị khác〉; end; end; end;
Thuật toán quay lui sẽ bắt đầu bằng lời gọi Try(1)
1.3 Một số ví dụ cơ bản
1.3.1 Dạng 1 - tìm tất cả các nghiệm
1.3.1.1 Liệt kê các dãy nhị phân độ dài N
Để biểu diễn dãy nhị phân có độ dài N, ta sử dụng x[1 n] Quá trình liệt kê các dãy này bắt đầu bằng cách thử nghiệm các giá trị {0, 1} cho x[i] Đối với mỗi giá trị được gán cho x[i], ta tiếp tục thử các giá trị khả thi cho x[i+1].
Chương trình liệt kê bằng thuật toán quay lui có thể viết:
Liệt kê các dãy nhị phân độ dài n
OutputFile = 'BSTR.OUT'; max = 30; var x: array[1 max] of Integer; n: Integer; f: Text;
{********************************} procedure PrintResult; {In cấu hình tìm được, do thủ tục tìm đệ quy Try gọi khi tìm ra một cấu hình} var i: Integer; begin for i := 1 to n do Write(f, x[i]);
The procedure `Try(i: Integer)` explores possible values for the array element `x[i]` It iterates through potential assignments of `0` and `1` to `x[i]` If the current index `i` equals `n`, it prints the result Otherwise, it recursively calls itself to evaluate the next index, `i + 1`, continuing the search for valid configurations of the array.
Try(1); {Thử các cách chọn giá trị x[1]}
1.3.1.2 Liệt kê các tập con k phần tử Để liệt kê các tập con k phần tử của tập S = {1, 2, …, n} ta có thể đưa về liệt kê các cấu hình x[1 n], ở đây các x[i] ∈ S và x[1] < x[2] < … < x[k] Ta có nhận xét: x[k] ≤ n x[k-1] ≤ x[k] - 1 ≤ n - 1
Từ đó suy ra x[i-1] + 1 ≤ x[i] ≤ n - k + i (1 ≤ i ≤ k) ở đây ta giả thiết có thêm một số x[0] = 0 khi xét i = 1.
Chúng ta sẽ xem xét tất cả các cách chọn x[1] từ 1 đến n - k + 1, sau đó tiếp tục chọn x[2] từ x[1] + 1 đến n - k + 2 Quá trình này sẽ tiếp tục cho đến khi chọn được x[k], từ đó chúng ta có một cấu hình cần liệt kê.
Liệt kê các tập con k phần tử program Combination; const
OutputFile = 'SUBSET.OUT'; max = 30; var x: array[0 max] of Integer; n, k: Integer; f: Text;
{********************************} procedure PrintResult; (*In ra tập con {x[1], x[2], …, x[k]}*) var i: Integer; begin
Write(f, '{'); for i := 1 to k - 1 do Write(f, x[i], ', ');
{********************************} procedure Try(i: Integer); {Thử các cách chọn giá trị cho x[i]} var j: Integer; begin for j := x[i - 1] + 1 to n - k + i do begin x[i] := j; if i = k then PrintResult else Try(i + 1); end; end;
1.3.1.3 Bài toán phân tích số
Cho một số nguyên dương n ≤ 30, nhiệm vụ là tìm tất cả các cách phân tích số n thành tổng của các số nguyên dương Các cách phân tích được coi là giống nhau nếu chúng là hoán vị của nhau, do đó chỉ tính là một cách duy nhất.
Ta sẽ lưu nghiệm trong mảng x, ngoài ra có một mảng t Mảng t xây dựng như sau: t[i] sẽ là tổng các phần tử trong mảng x từ x[1] đến x[i]: t[i] := x[1] + x[2] + … + x[i].
Khi liệt kê các dãy x có tổng các phần tử đúng bằng n, để tránh sự trùng lặp ta đưa thêm ràng buộc x[i-1] ≤ x[i].
Vì số lượng phần tử thực sự trong mảng x không cố định, thủ tục PrintResult cần có thêm tham số để xác định số lượng phần tử sẽ được in ra.
Thủ tục đệ quy Try(i) sẽ thử các giá trị có thể nhận của x[i] (x[i]
Khi nào thì in kết quả và khi nào thì gọi đệ quy tìm tiếp ?
Lưu ý rằng t[i - 1] là tổng của tất cả các phần tử từ x[1] đến x[i-
- Khi t[i] = n tức là (x[i] = n - t[i - 1]) thì in kết quả
- Khi tìm tiếp, x[i+1] sẽ phải lớn hơn hoặc bằng x[i] Mặt khác t[i+1] là tổng của các số từ x[1] tới x[i+1] không được vượt quá n
Ví dụ đơn giản khi n = 10 thì chọn x[1] =6, 7, 8, 9 là việc làm vô nghĩa vì như vậy cũng không ra nghiệm mà cũng không chọn tiếp x[2] được nữa.
Đệ quy tìm tiếp được thực hiện khi giá trị x[i] được chọn cho phép chọn thêm một phần tử khác lớn hơn hoặc bằng nó mà không làm tổng vượt quá n Kết quả chỉ được in ra khi x[i] bằng đúng số thiếu hụt của tổng i-1 phần tử đầu so với n.
Vậy thủ tục Try(i) thử các giá trị cho x[i] có thể viết như sau: (để tổng quát cho i = 1, ta đặt x[0] = 1 và t[0] = 0).
- Xét các giá trị của x[i] từ x[i - 1] đến (n - t[i-1]) div 2, cập nhật t[i] := t[i - 1] + x[i] và gọi đệ quy tìm tiếp.
- Cuối cùng xét giá trị x[i] = n - t[i-1] và in kết quả từ x[1] đến x[i].
Input: file văn bản ANALYSE.INP chứa số nguyên dương n ≤ 30 Output: file văn bản ANALYSE.OUT ghi các cách phân tích số n.
Bài toán phân tích số program Analyses; const
OutputFile = 'ANALYSE.OUT'; max = 30; var n: Integer; x: array[0 max] of Integer; t: array[0 max] of Integer; f: Text;
{********************************} procedure Init; {Khởi tạo} begin
{********************************} procedure PrintResult(k: Integer); var i: Integer; begin
Write(f, n, ' = '); for i := 1 to k - 1 do Write(f, x[i], '+');
{********************************} procedure Try(i: Integer); var j: Integer; begin for j := x[i - 1] to (n - T[i - 1]) div 2 do {Trường hợp còn chọn tiếp x[i+1]} begin x[i] := j; t[i] := t[i - 1] + j;
{Nếu x[i] là phần tử cuối thì nó bắt buộc phải là n-T[i-1], in kết quả}
Bài toán máy rút tiền tự động ATM
Một máy rút tiền tự động ATM có n (n< ) tờ tiền có giá trị t1,t2,…,tn Hãy đưa ra một cách trả với số tiền đúng bằng S.
Dữ liệu vào từ file “ATM.INP” có dạng:
- dòng đầu là 2 số n và s
Kết quả ra file “ATM.OUT” có dạng: Nếu có thể trả đúng S thì đưa ra cách trả, nếu không thì ghi -1
Nghiệm của bài toán là một dãy nhị phân độ dài n, trong đó thành phần thứ I bằng 1 nếu tờ tiền thứ I được sử dụng để trả, bằng
0 trong trường hợp ngược lại
X=(x1,x2,…,xn) là nghiệm nếu x1 x t1+ x2 x t2+…+ xn x tn=s
Chương trình sử dụng biến ok để kiểm soát quá trình tìm nghiệm Ban đầu, ok được khởi tạo với giá trị okSE, cho thấy chưa có nghiệm Khi tìm thấy nghiệm, ok sẽ được gán giá trị TRUE, và nếu ok=TRUE, quá trình tìm kiếm sẽ dừng lại.
Máy rút tiền tự động ATM
OutputFile ='ATM.OUT'; type vector =array[1 MAX]of longint; var t :array[1 MAX]of longint; x,xs :vector; n,s,sum :longint; ok :boolean;
{********************************} procedure input; var f :text; i :longint; begin assign(f, InputFile); reset(f); readln(f,n, s); for i:=1 to n do read(f,t[i]); close(f); end;
{********************************} procedure check(x:vector); var i :longint; f :text; begin if sum = s then begin xs:=x; ok:=true; end; end;
{********************************} procedure printResult; var i :longint; f :text; begin assign(f, OutputFile); rewrite(f); if ok then begin for i:=1 to n do if xs[i]=1 then write(f,t[i],' '); end else write(f,'-1'); close(f); end;
The procedure `Try(i: longint)` explores all possible combinations of binary values for the array `x` It iterates through values of `j` from 0 to 1, updating the sum based on the current index `i` and the corresponding weight `t[i]` If the index `i` reaches the total `n`, it calls the `check(x)` function to validate the current combination If the cumulative `sum` does not exceed the limit `s`, the procedure recursively calls itself with the next index The process halts if a valid solution is found, ensuring efficiency by avoiding unnecessary checks After each iteration, the contribution of the current value is subtracted from the sum to backtrack correctly.
{********************************} begin input; ok:se; sum:=0;
Ngoài ra ta có cách thể hiện khác của thuật toán quay lui máy đếm tiền tự động ATM
OutputFile ='ATM.OUT'; var t:array[1 100] of longint; a,x:array[1 100] of 0 1; i,n:integer; sum,s:int64; kt:boolean; fi,fo:text;
{********************************} procedure xuat; var i:integer; begin sum:=0; for i:=1 to n do sum:=sum+a[i]*t[i]; if sum=s then begin x:= a; kt:=true; exit; end; end;
{********************************} procedure try(i:integer); var j:integer; begin for j:=0 to 1 do if ktse then begin a[i]:=j; if i=n then xuat else try(i+1); end; end;
The program reads input data from a file, processes it, and writes the results to an output file It initializes variables and checks a condition before iterating through the data If a specific condition is met, it writes certain values to the output; otherwise, it outputs -1 Finally, the output file is closed after the operation.
1.3.3 Dạng 3 – Tìm nghiệm tối ưu thỏa mãn điều kiện
Trong dịp lễ 30 tháng 4 và 1 tháng 5 vừa qua, do trùng với ngày giỗ tổ Hùng Vương 10 tháng 3 âm lịch, số ngày nghỉ lễ đã tăng lên Điều này đã dẫn đến lượng khách du lịch tăng kỷ lục, gây ra tình trạng “cháy phòng” tại các khách sạn.