Download as pdf or txt
Download as pdf or txt
You are on page 1of 12

Quy hoạch động (Dynamic

Programing)
Quy hoạch động là gì ?
QHĐ là kĩ thuật được được dùng khi có một công thức và một (hoặc một vài) trạng
thái bắt đầu. Một bài toán được tính bởi các bài toán nhỏ hơn đã tìm ra trước
đó. QHĐ có độ phức tạp đa thức nên sẽ chạy nhanh hơn quay lui và duyệt trâu.

Quy hoạch động (Dynamic Programming) hoạt động như thế


nào?
1. Xác định các bài toán con: Chia bài toán chính thành các bài toán con nhỏ
hơn và độc lập.

2. Lưu trữ các lời giải: Giải quyết từng bài toán con và lưu trữ lời giải vào một
bảng hoặc mảng.

3. Xây dựng lời giải: Sử dụng các lời giải đã lưu trữ để xây dựng lời giải cho bài
toán chính.

4. Tránh dư thừa: Bằng cách lưu trữ các lời giải, quy hoạch động đảm bảo rằng
mỗi bài toán con chỉ được giải một lần, giúp giảm thời gian tính toán.

Khi nào thì sử dụng quy hoạch động ?


Quy hoạch động là một kỹ thuật tối ưu hóa được sử dụng khi giải quyết các bài
toán có các đặc điểm sau:

1. Cấu trúc con tối ưu:


Cấu trúc con tối ưu nghĩa là chúng ta kết hợp các kết quả tối ưu của các bài
toán con để đạt được kết quả tối ưu của bài toán lớn hơn.
Ví dụ:
Xét bài toán tìm đường đi có chi phí tối thiểu trong một đồ thị có trọng số từ
một nút nguồn đến một nút đích. Chúng ta có thể chia bài toán này thành các
bài toán con nhỏ hơn:

Tìm đường đi có chi phí tối thiểu từ nút nguồn đến mỗi nút trung gian.

Quy hoạch động (Dynamic Programing) 1


Tìm đường đi có chi phí tối thiểu từ mỗi nút trung gian đến nút đích.

Giải pháp cho bài toán lớn hơn (tìm đường đi có chi phí tối thiểu từ nút nguồn
đến nút đích) có thể được xây dựng từ các giải pháp của những bài toán con
này.

2. Các bài toán con trùng lặp:


Các bài toán con giống nhau được giải quyết lặp đi lặp lại ở các phần khác
nhau của bài toán.
Ví dụ:
Xét bài toán tính dãy Fibonacci. Để tính số Fibonacci tại chỉ số n, chúng ta cần
tính các số Fibonacci tại các chỉ số n−1 và n−2. Điều này có nghĩa là bài toán
con tính số Fibonacci tại chỉ số n−1 được sử dụng hai lần trong giải pháp của
bài toán lớn hơn là tính số Fibonacci tại chỉ số n.

Các phương pháp của Quy hoạch động (Dynamic Programming)


Quy hoạch động có thể được thực hiện bằng hai phương pháp:

1. Phương pháp Top-Down (Memoization):


Trong phương pháp
Top-Down, còn được gọi là memoization, chúng ta bắt đầu với lời giải cuối
cùng và đệ quy chia nhỏ nó thành các bài toán con. Để tránh các tính toán dư
thừa, chúng ta lưu trữ kết quả của các bài toán con đã được giải vào một bảng
ghi nhớ.
Phân tích phương pháp Top-Down:

Bắt đầu với lời giải cuối cùng và đệ quy chia nhỏ thành các bài toán con.

Lưu trữ các lời giải của các bài toán con vào một bảng để tránh tính toán
dư thừa.

Phù hợp khi số lượng bài toán con lớn và nhiều trong số chúng được tái sử
dụng.

2. Phương pháp Bottum-Up (Tabulation):


Trong phương pháp
Bottum-Up, còn được gọi là tabulation, chúng ta bắt đầu với các bài toán con
nhỏ nhất và dần dần xây dựng lên lời giải cuối cùng. Chúng ta lưu trữ kết quả

Quy hoạch động (Dynamic Programing) 2


của các bài toán con đã được giải vào một bảng để tránh các tính toán dư
thừa.
Phân tích phương pháp Bottum-Up:

Bắt đầu với các bài toán con nhỏ nhất và dần dần xây dựng lên lời giải cuối
cùng.

Điền vào một bảng các lời giải cho các bài toán con theo cách từ dưới lên.

Phù hợp khi số lượng bài toán con nhỏ và lời giải tối ưu có thể được tính
trực tiếp từ các lời giải của các bài toán con nhỏ hơn.

Thuật toán Quy hoạch động (Dynamic Programming)


Quy hoạch động là một kỹ thuật thuật toán giải quyết các bài toán phức tạp bằng
cách chia chúng thành các bài toán con nhỏ hơn và lưu trữ các lời giải của chúng
để sử dụng trong tương lai. Phương pháp này đặc biệt hiệu quả cho các bài toán
có các bài toán con trùng lặp và cấu trúc con tối ưu.

Lý thuyết nãy giờ nhiều rồi !!! Thực hành thôi


Bài 1: Tính số Fibonacci

Duyệt trâu (Brute force)

#include<bits/stdc++.h>
using namespace std;

int n;

Quy hoạch động (Dynamic Programing) 3


int fibo(int i){
if (i == 0) return 0;
if (i == 1) return 1;
return fibo(i-1) + fibo(i-2);
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
n = 6;
cout << fibo(n);
}

ĐPT: O(2^n)
Đệ quy có nhớ (Memoization)

Quy hoạch động (Dynamic Programing) 4


#include<bits/stdc++.h>
using namespace std;

int n;
vector<int>memo(1000, -1); // bài toán có giá trị -1 là những bà

int fibo(int i){


if (i == 0) return 0;
if (i == 1) return 1;
if (memo[i] != -1) return memo[i];
return memo[i] = fibo(i-1) + fibo(i-2);
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
n = 6;
cout << fibo(n);
}

Quy hoạch động (Dynamic Programing) 5


ĐPT: O(n)
Dạng bảng (Tablutation)

#include<bits/stdc++.h>
using namespace std;

int n;
vector<int>dp(1000,0);
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
n = 6;
//Những bài toán cơ sở
dp[0] = 0;
dp[1] = 1;
//Tính những bài toán lớn hơn từ bài toán những bài toán nhỏ
for(int i = 2; i <= n; i++) dp[i] = dp[i-1] + dp[i-2];

Quy hoạch động (Dynamic Programing) 6


cout << dp[n];
}

ĐPT: O(n)

Note:
dp là viết tắt của Dynamic Programming

Bài 2: Xâu con chung dài nhất (Longest Common Subsequence _ Link bài)

Duyệt trâu:

#include <bits/stdc++.h>
using namespace std;

string a,b;

int lcs(int i, int j)


{
if (i < 0 || j < 0)
return 0;
if (a[i] == b[j])
return 1 + lcs(i - 1, j - 1);
return max(lcs(i - 1, j), lcs(i, j - 1));
}
void solve()
{
cin >> a >> b;
cout << lcs(a.size() - 1, b.size() - 1);
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);

Quy hoạch động (Dynamic Programing) 7


solve();
}

Đệ quy có nhớ:

#include <bits/stdc++.h>
using namespace std;

string a, b;
vector<vector<int>> memo(1005, vector<int>(1005, -1));
int lcs(int i, int j)
{
if (i < 0 || j < 0)
return 0;
if (memo[i][j] != -1)
return memo[i][j];
if (a[i] == b[j])
return memo[i][j] = 1 + lcs(i - 1, j - 1);
else
return memo[i][j] = max(lcs(i - 1, j), lcs(i, j - 1));
}
void solve()
{
cin >> a >> b;
cout << lcs(a.size() - 1, b.size() - 1);
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
}

Duyệt:

Quy hoạch động (Dynamic Programing) 8


#include <bits/stdc++.h>
using namespace std;

string a, b;
vector<vector<int>> dp(1005, vector<int>(1005, 0));

void solve()
{
cin >> a >> b;
int n = a.size();
int m = b.size();
a = " " + a;
b = " " + b;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if (a[i] == b[j]) dp[i][j] = dp[i-1][j-1]+1;
else dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}
}
cout << dp[n][m];
}

int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
}

Bài 3: Dãy còn tăng dài nhất (Longest Increasing Subsequence _ Link bài)

Duyệt trâu:

Quy hoạch động (Dynamic Programing) 9


#include<bits/stdc++.h>
using namespace std;

int a[1005];
int n;
const int inf = 1e9 + 7;

int lis(int cur){


int res = 0;
for(int i = cur-1; i >= 0; i--){
if (a[cur] >= a[i]) res = max(res,lis(i)+1);
}
return res;
}
void solve(){
cin >> n;
for(int i = 0; i < n; i++) cin >> a[i];
a[n] = inf;
cout << lis(n);
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
}

Đệ quy có nhớ:

#include <bits/stdc++.h>
using namespace std;

int a[1005];
int n;

Quy hoạch động (Dynamic Programing) 10


const int inf = 1e9 + 7;
vector<int> memo(1005, -1);
int lis(int cur)
{
int res = 0;
if (memo[cur] != -1)
return memo[cur];
for (int i = cur - 1; i >= 0; i--)
{
if (a[cur] >= a[i])
res = max(res, lis(i) + 1);
}
memo[cur] = res;
return memo[cur];
}
void solve()
{
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
a[n] = inf;
cout << lis(n);
}
signed main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
}

Duyệt

#include <bits/stdc++.h>
using namespace std;

Quy hoạch động (Dynamic Programing) 11


int a[1005];
int n;
const int inf = 1e9 + 7;
vector<int> dp(1005, 0);

void solve()
{
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
a[n] = inf;
int res = 0;
for(int i = 0; i <= n; i++){
res = 0;
for(int j = 0; j < i; j++){
if (a[j] <= a[i]) res = max(res,dp[j] + 1);
}
dp[i] = res;
}
cout << dp[n];
}
signed main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
}

Quy hoạch động (Dynamic Programing) 12

You might also like