Mẹo nhỏ: Để tìm kiếm chính xác các bài viết của Vuihecungchocopie.vn, hãy search trên Google với cú pháp: "Từ khóa" + "vuihecungchocopie". (Ví dụ: công thức giải rubik 3x3 vuihecungchocopie). Tìm kiếm ngay
25 lượt xem

Phân tích thuật toán Quy hoạch động – Một thuật toán thần thánh | TopDev

Bạn đang quan tâm đến Phân tích thuật toán Quy hoạch động – Một thuật toán thần thánh | TopDev phải không? Nào hãy cùng Vuihecungchocopie đón xem bài viết này ngay sau đây nhé, vì nó vô cùng thú vị và hay đấy!

Trong bài viết này, topdev sẽ giới thiệu cho bạn một thuật toán thiêng liêng: thuật toán lập trình động . Nếu bạn tham gia các cuộc thi viết mã, bạn phải biết thuật toán này.

Gần một nửa số bài kiểm tra trong các cuộc thi viết mã yêu cầu lập trình động. Tất nhiên, có những cách khác để giải quyết vấn đề này. Nhưng vì các cuộc thi viết mã bị giới hạn về thời gian và bộ nhớ của chương trình, nên các thuật toán hiệu quả là rất quan trọng. Trong trường hợp này, lập trình động là một trong những thuật toán phổ biến nhất.

Bạn đang xem: Quy hoạch động là gì

Các thuật toán lập trình động được ưa thích hơn vì ban đầu, các vấn đề có nhiều dạng và bạn phải suy nghĩ rất nhiều để tìm ra giải pháp. Không có công thức tiêu chuẩn duy nhất phù hợp cho tất cả các vấn đề. Do tính phổ biến của nó, bạn phải rất thành thạo thuật toán này nếu muốn đạt điểm cao trong cuộc thi.

Khi nào sử dụng thuật toán lập trình động

Khi nào chúng ta cần lập trình động? Đây là một câu hỏi khó trả lời. Không có công thức cho loại câu hỏi này.

Tuy nhiên, bạn có thể nghĩ đến một số thuộc tính của vấn đề trong lập trình động. Dưới đây là hai trong số những điểm nổi bật nhất:

  • Vấn đề chồng chéo bài toán con.
  • Bài toán cấu trúc con tối ưu.

Thông thường, đối với các vấn đề với hai thuộc tính này, chúng ta có thể sử dụng lập trình động. Một câu hỏi rất thú vị là không sử dụng lập trình động có ổn không? Câu trả lời là có, nhưng nếu bạn tham gia kỳ thi mã hóa, bạn chắc chắn sẽ trượt. Để hiểu rõ hơn, chúng ta sẽ xem xét từng thuộc tính này trong các phần sau

Vấn đề trùng lặp

Tương tự như thuật toán chia để trị, lập trình động cũng chia một bài toán lớn thành các bài toán con nhỏ hơn. Lập trình động được sử dụng khi các bài toán con này được gọi lặp đi lặp lại. Phương pháp lập trình động lưu kết quả của bài toán con này và không cần tính toán lại khi được gọi, do đó giảm thời gian tính toán.

Lập trình động không áp dụng (hay đúng hơn là không hoạt động) khi các bài toán con không trùng lặp. Ví dụ, với thuật toán tìm kiếm nhị phân, lập trình động hoàn toàn không thể tối ưu hóa được, bởi vì mỗi lần giải một bài toán lớn thành các bài toán con, mỗi bài toán chỉ cần giải một lần và không được gọi lại.

Một ví dụ rất điển hình về vấn đề chồng chéo là vấn đề tính toán các số Fibonacci . Bài toán này nổi tiếng đến mức chúng ta có thể tính số Fibonacci theo công thức sau:

Nếu chúng ta tính toán như trên, chúng ta sẽ có nhiều bài toán con cần tính toán lại, thường là các số fib (0) và fib (1).

Lập trình động là một phương pháp có thể giúp chúng tôi tối ưu hóa tính toán này. Mỗi bài toán con (số lượng sợi) được lưu trước khi tính toán các bài toán con lớn hơn. Do đó, số lượng tính toán được giảm đáng kể và mỗi bài toán con chỉ cần được tính toán chỉ một lần .

Một ví dụ về lập trình động cho vấn đề này như sau:

Bạn đã thấy sức mạnh phi thường của lập trình động qua ví dụ trên chưa? Đó là lý do tại sao nó rất phổ biến trong các cuộc thi lập trình, bởi vì cả thời gian và bộ nhớ đều có hạn (và thường là nhỏ).

Xem thêm: Tình yêu quê hương đất nước là gì

Tìm một công việc lập trình

Cấu trúc con tối ưu

Cấu trúc con tối ưu là một thuộc tính của tập hợp các giải pháp cho một vấn đề lớn sẽ là giải pháp cho một vấn đề nhỏ.

Tôi đưa ra một ví dụ để bạn dễ hiểu hơn:

Trong bài toán tìm đường đi ngắn nhất trong đồ thị, nếu một nút x nằm trên đường đi ngắn nhất giữa hai nút u và v thì đường đi ngắn nhất từ ​​u đến v là tổng các đường đi ngắn nhất từ ​​u đến v x và đường đi ngắn nhất từ ​​x đến v. Một số thuật toán tìm đường dẫn đồ thị (có lẽ nổi tiếng nhất là dijkstra) dựa trên thuộc tính này và lập trình động cũng được áp dụng.

Thuộc tính cấu trúc con tối ưu là rất quan trọng. Nó cho phép chúng tôi giải quyết các vấn đề lớn dựa trên các vấn đề phụ đã được giải quyết. Nếu không có thuộc tính này, chúng ta không thể áp dụng lập trình động.

Không phải tất cả các vấn đề đều có thuộc tính cấu trúc con tối ưu này. Ví dụ về hình ảnh bên dưới:

Thuật toán Quy hoạch động

Đường dẫn dài nhất từ q -> t sẽ là q – & gt; r – & gt; t hoặc q -> s – & gt; t. Nhưng khác với bài toán đường đi ngắn nhất, đường đi dài nhất không phải là sự kết hợp của các đường dẫn thành phần, do đó bài toán này không có cấu trúc con tối ưu.

Ví dụ: dòng q – & gt; r – & gt; t không phải là sự kết hợp của con đường dài nhất từ ​​q ->; r và con đường dài nhất từ ​​r -> t. Bởi vì, đường đi dài nhất q -> r phải là q ->; s – & gt; t- & gt; r và đường đi dài nhất từ ​​r -> t phải là r -> q – & gt; s – & gt; t.

Một số sự cố lập trình động

Trong phần này, chúng tôi sẽ sử dụng một số ví dụ cụ thể để làm quen với lập trình động. Chúng ta sẽ xem cách áp dụng lập trình động cho một vấn đề cụ thể và sử dụng nó để hiểu rõ hơn về các thuộc tính trong phần trước.

Ví dụ 1: Bài toán về đồng xu cổ điển

Đây là một ví dụ điển hình về việc học lập trình động. Có thể có nhiều câu nói khác nhau, nhưng về cơ bản nó sẽ giống như sau.

Giả sử chúng ta có n đồng xu với trọng lượng w1, w2, …, wn, vấn đề là tìm số đồng xu nhỏ nhất sao cho tổng khối lượng của chúng là s. Tất nhiên, số lượng xu là không giới hạn.

Giả sử chúng ta có n đồng xu với trọng lượng w1, w2, …, wn, vấn đề là tìm số đồng xu nhỏ nhất sao cho tổng khối lượng của chúng là s. Tất nhiên, số lượng xu là không giới hạn.

Xem thêm: Sinh sản vô tính là gì? Các hình thức sinh sản vô tính ở sinh vật

Đối với vấn đề này, chúng tôi cần xây dựng và giải quyết các vấn đề chồng chéo. Ví dụ, bài toán con dp (p) với mọi p <= s là bài toán tìm số đồng xu nhỏ nhất sao cho nó có khối lượng p. dp (p) = k là số đồng xu tối thiểu.

Chúng tôi sẽ áp dụng các phương pháp lập trình động bắt đầu với bài toán con dp (0) và sau đó tiến hành các bài toán con lớn hơn. Các giải pháp cho các bài toán con sẽ được xây dựng tuần tự cho đến khi chúng ta đạt được dp (các) bài toán, là kết quả của bài toán lớn. Một điều cần nhớ với kỹ thuật này là nếu chúng ta không giải được bài toán con trước đó thì bài toán con tiếp theo sẽ không được giải.

Cuối cùng, phần khó nhất của bất kỳ bài toán lập trình động nào là trả lời câu hỏi: đâu là cấu trúc con tối ưu của bài toán. Hay nói cách khác là cách kết hợp những vấn đề nhỏ để giải quyết những vấn đề lớn. Với ví dụ kinh điển này, mọi thứ sẽ tương đối đơn giản, nhưng đối với những bài toán phức tạp hơn, chúng ta cần tư duy và tính toán nhiều hơn.

Quay lại câu hỏi của chúng tôi. Giả sử p lần lượt là tổng khối lượng của các đồng xu nặng v1, v2, …, vj. Để có khối lượng p, chúng ta cần thêm một số đồng xu có khối lượng u bằng 1 vào khối lượng q sao cho q + u = p. Tất nhiên, đối với bài toán con dp (q), chúng ta đã có một lời giải, vì vậy chúng ta biết cần bao nhiêu đồng xu cho dp (p). Vì có nhiều đồng tiền u (nhiều nhưng hữu hạn), chúng ta có thể cần nhiều bài toán con trước đó, và dp (p) là tổng tối thiểu của các bài toán con này.

Ví dụ về n = 3, s = 11, w = [1, 3, 5].

  • Bắt đầu với bài toán con 0, chúng ta có dp (0) = 0
  • Đối với bài toán con 1, chúng ta có thể thêm 1 đồng (trọng số 1) từ 0 đồng. Vậy dp (1) = dp (0) + 1 = 1.
  • Đối với bài toán con 2, 1 đồng chỉ có thể thêm 1 đồng (trọng lượng 1). Vậy dp (2) = dp (1) + 1 = 2.
  • Đối với bài toán con 3, chúng ta có thể thêm 1 xu 3 đến 0 xu hoặc 1 xu 1 đến 2 xu. Rõ ràng, phương pháp đầu tiên tạo ra kết quả nhỏ hơn. Vì vậy, dp (3) = min (dp (2) + 1, dp (0) + 1) = min (3, 1) = 1
  • tiếp tục cho đến khi vấn đề s là câu trả lời chúng ta cần tìm.

Về mặt triển khai, lập trình động thường lưu trữ các kết quả trong một mảng. Trong ví dụ của chúng ta, mảng dp [0..s] sẽ lưu trữ kết quả cho mỗi bài toán con. Nói cách khác, dp [p] = k có nghĩa là cần ít nhất k đồng xu để có khối lượng mà toàn bộ mảng sẽ được tính thông qua vòng lặp. Đoạn mã sau đây mô tả toàn bộ quá trình.

Ví dụ 2: Chuỗi con chung dài nhất (lcs)

Một ví dụ đơn giản khác, cũng là một vấn đề rất nổi tiếng.

Đối với hai chuỗi. Tìm độ dài của chuỗi con chung nhỏ nhất giữa chúng. Ví dụ: đối với 2 chuỗi “quetzalcoatl” và “tezcatlipoca”, chuỗi con chung dài nhất sẽ là “ezaloa” có độ dài 6.

Với sự cố này, chúng tôi lần lượt giải quyết các vấn đề phụ sau:

Lấy i ký tự đầu tiên từ chuỗi đầu tiên, lấy j ký tự đầu tiên từ chuỗi thứ hai và tìm độ dài của chuỗi chung dài nhất giữa hai chuỗi con được trích xuất. Dễ thấy rằng nghiệm của mỗi bài toán con sẽ phụ thuộc vào i và j, dp (i, j). Đối với các bài toán lớn, các bài toán con được giải lần lượt bắt đầu từ dp (0, 0), và độ dài của chuỗi rút ra được tăng dần cho đến khi thu được toàn bộ chuỗi của bài toán.

Hãy bắt đầu từng vấn đề phụ một. Tất nhiên, nếu một trong hai chuỗi trống, chuỗi con chung của chúng cũng trống. Vậy dp (0, j) = dp (i, 0) = 0. Nếu cả i và j đều dương, chúng ta cần xem xét một số trường hợp.

  1. Nếu ký tự cuối cùng của chuỗi đầu tiên không tồn tại trong chuỗi con chung dài nhất, nó có thể bị bỏ qua mà không ảnh hưởng đến kết quả. Công thức ở đây sẽ là dp (i, j) = dp (i – 1, j).
  2. Tương tự như trên, ký tự cuối cùng của chuỗi thứ hai không ảnh hưởng đến kết quả. Khi đó dp (i, j) = dp (i, j – 1).
  3. Trường hợp cuối cùng, nếu hai ký tự cuối cùng của hai chuỗi x1, x2 xuất hiện trong chuỗi con chung dài nhất. Tất nhiên, hai ký tự này phải là một để điều này xảy ra, tức là x1 == x2. Trong trường hợp này, việc xóa một trong hai ký tự đó sẽ làm cho chuỗi con chung dài nhất 1 ký tự ngắn hơn. Vì vậy, rõ ràng là dp (i, j) = dp (i – 1, j – 1) + 1.

Trong cả ba trường hợp trên, chúng ta phải chọn cái nào sẽ tạo ra chuỗi con chung dài nhất (đối với bài toán này, chỉ cần đưa ra độ dài này là đủ).

Về cách triển khai, dp sẽ được lưu trữ trong một mảng hai chiều. Kết quả của mảng này sẽ được tính toán thông qua hai lớp vòng lặp. Lưu ý rằng chúng ta cần tạo các vòng lặp để chúng ta sẽ giải quyết các bài toán con lần lượt theo thứ tự tăng dần. Vì mỗi bài toán con dp (i, j) phụ thuộc vào các bài toán con trước dp (i – 1, j), dp (i, j – 1), dp (i – 1, j – 1).

Xem thêm: Nơi đăng ký khám chữa bệnh ban đầu là gì?

Công khai: VUIHECUNGCHOCOPIE.VN là trang web Tổng hợp Ẩm Thực - Game hay và Thủ Thuật hàng đầu VN, thuộc Chocopie Vietnam. Mời thính giả đón xem.

Chúng tôi trân trọng cảm ơn quý độc giả luôn ủng hộ và tin tưởng!

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *