Bài toán cái túi
Có n đồ vật, vật thứ i nặng wi và có giá trị vi. Cái túi chịu được nhiều nhất W. Chọn các vật để tổng giá trị lớn nhất. Thử mọi cách chọn là 2n. Khi W không quá lớn (vài trăm nghìn), quy hoạch động theo khối lượng giải trong khoảng n × W bước.
Cái túi 0/1
Phần tiêu đề “Cái túi 0/1”Mỗi vật chọn hoặc không (0 hoặc 1 lần). Gọi f[i][c] là giá trị lớn nhất khi chỉ xét i vật đầu và túi chịu được c. Với vật thứ i:
- không chọn: f[i - 1][c];
- chọn (nếu wi ≤ c): f[i - 1][c - wi] + vi.
f[i][c] là lớn hơn của hai cách. Ba vật (nặng, giá trị): (1, 1), (3, 4), (4, 5), W = 7:
| c | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| chưa có vật | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| thêm (1, 1) | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| thêm (3, 4) | 0 | 1 | 1 | 4 | 5 | 5 | 5 | 5 |
| thêm (4, 5) | 0 | 1 | 1 | 4 | 5 | 6 | 6 | 9 |
Đáp số 9 (chọn vật 2 và vật 3).
Một mảng, đi ngược. Hàng i chỉ cần hàng i - 1, và ô c chỉ đọc các ô nhỏ hơn hoặc bằng c của hàng trước. Dùng một mảng f và cho c chạy từ W về wi: khi tính f[c], ô f[c - wi] chưa bị sửa ở lượt này, vẫn là giá trị của hàng trước. Nhờ vậy mỗi vật chỉ được chọn tối đa một lần.
import sysdu_lieu = sys.stdin.read().split()n, W = int(du_lieu[0]), int(du_lieu[1])f = [0] * (W + 1) # f[c]: giá trị lớn nhất với túi chịu cfor i in range(n): w, v = int(du_lieu[2 + 2 * i]), int(du_lieu[3 + 2 * i]) for c in range(W, w - 1, -1): # đi ngược: mỗi vật dùng tối đa một lần if f[c - w] + v > f[c]: f[c] = f[c - w] + vprint(f[W])#include <iostream>#include <vector>#include <algorithm>using namespace std;
int main() { int n, W; cin >> n >> W; vector<long long> f(W + 1, 0); // f[c]: giá trị lớn nhất với túi chịu c for (int i = 0; i < n; i++) { int w; long long v; cin >> w >> v; for (int c = W; c >= w; c--) f[c] = max(f[c], f[c - w] + v); // đi ngược } cout << f[W] << "\n"; return 0;}Cái túi không giới hạn
Phần tiêu đề “Cái túi không giới hạn”Nếu mỗi loại vật lấy được bao nhiêu cũng được, cho c chạy xuôi từ wi lên W. Khi tính f[c], ô f[c - wi] đã được cập nhật ở lượt này (có thể đã chứa vật i), nên vật i được dùng thêm lần nữa. Chỉ đổi chiều vòng lặp mà ý nghĩa thay đổi hẳn.
| Loại | Chiều của c |
|---|---|
| mỗi vật tối đa một lần (0/1) | từ W về wi |
| mỗi vật không giới hạn | từ wi lên W |
Có giới hạn số lượng: tách nhị phân
Phần tiêu đề “Có giới hạn số lượng: tách nhị phân”Loại i có ki cái. Tách thành các “gói” có số lượng 1, 2, 4, …, 2p và phần còn lại, rồi coi mỗi gói là một vật 0/1. Mọi số lượng từ 0 đến ki đều ghép được từ các gói (giống biểu diễn nhị phân). Ví dụ k = 13 tách thành 1, 2, 4, 6: số 9 = 1 + 2 + 6, số 13 = 1 + 2 + 4 + 6. Loại có k cái chỉ thành khoảng log2k vật thay vì k vật.
Các bài cùng khuôn
Phần tiêu đề “Các bài cùng khuôn”| Bài | Mảng f nghĩa là | Công thức |
|---|---|---|
| tổng tập con: đạt được S không | f[c]: đạt được tổng c không | f[c] = f[c] hoặc f[c - ai] (đi ngược) |
| số cách đổi tiền (không kể thứ tự) | f[c]: số cách tạo c | f[c] += f[c - ai] (đi xuôi, vòng loại tiền ở ngoài) |
| số tờ ít nhất | f[c]: số tờ ít nhất tạo c | f[c] = min(f[c], f[c - ai] + 1) (đi xuôi) |
| W rất lớn, giá trị nhỏ | g[v]: khối lượng nhỏ nhất đạt giá trị v | đổi vai khối lượng và giá trị |
Luyện tập
Phần tiêu đề “Luyện tập”Gồm 17 bài, chia thành 4 nhóm từ dễ đến khó. Mỗi bài có một dữ liệu kiểm tra: các bạn chạy chương trình của mình với dữ liệu đó rồi so với đáp số.
Nhóm 1. Ba kiểu cái túi
Phần tiêu đề “Nhóm 1. Ba kiểu cái túi”Bài 1. Cái túi 0/1
Phần tiêu đề “Bài 1. Cái túi 0/1”Dòng đầu có n, W (1 ≤ n ≤ 100, 0 ≤ W ≤ 105), n dòng sau mỗi dòng có w, v (1 ≤ w ≤ 105, 1 ≤ v ≤ 109) là khối lượng và giá trị một vật. Mỗi vật chọn tối đa một lần, tổng khối lượng không quá W. In ra tổng giá trị lớn nhất.
Ví dụ:
| Nhập | In ra |
|---|---|
3 71 13 44 5 | 9 |
Kiểm tra: nhập
6 155 104 406 303 507 352 15Bài 2. Cái túi không giới hạn
Phần tiêu đề “Bài 2. Cái túi không giới hạn”Dòng đầu có n, W (1 ≤ n ≤ 100, 0 ≤ W ≤ 105), n dòng sau mỗi dòng có w, v (1 ≤ w ≤ 105, 1 ≤ v ≤ 109). Mỗi loại vật lấy được bao nhiêu cái cũng được, tổng khối lượng không quá W. In ra tổng giá trị lớn nhất.
Ví dụ:
| Nhập | In ra |
|---|---|
3 82 33 55 9 | 14 |
Kiểm tra: nhập
4 507 2010 3113 4017 55Bài 3. Cái túi có giới hạn số lượng
Phần tiêu đề “Bài 3. Cái túi có giới hạn số lượng”Dòng đầu có n, W (1 ≤ n ≤ 100, 0 ≤ W ≤ 104), n dòng sau mỗi dòng có w, v, k (1 ≤ w ≤ 104, 1 ≤ v ≤ 106, 1 ≤ k ≤ 104): loại thứ i có k cái, mỗi cái nặng w, giá trị v. Tổng khối lượng không quá W. In ra tổng giá trị lớn nhất.
Ví dụ:
| Nhập | In ra |
|---|---|
3 102 3 23 5 11 2 4 | 17 |
Kiểm tra: nhập
4 1007 20 310 31 513 40 14 11 12Bài 4. Túi phải đầy
Phần tiêu đề “Bài 4. Túi phải đầy”Dòng đầu có n, W (1 ≤ n ≤ 100, 0 ≤ W ≤ 105), n dòng sau mỗi dòng có w, v. Chọn mỗi vật tối đa một lần sao cho tổng khối lượng đúng bằng W. In ra tổng giá trị lớn nhất, hoặc -1 nếu không chọn được.
Ví dụ:
| Nhập | In ra |
|---|---|
3 62 34 75 9 | 10 |
Kiểm tra: nhập
5 125 104 406 303 507 35Nhóm 2. Tổng tập con
Phần tiêu đề “Nhóm 2. Tổng tập con”Bài 5. Có tạo được tổng S không
Phần tiêu đề “Bài 5. Có tạo được tổng S không”Dòng đầu có n, S (1 ≤ n ≤ 100, 0 ≤ S ≤ 105), dòng thứ hai có n số nguyên dương (ai ≤ 105). In ra YES nếu chọn được một số phần tử (mỗi phần tử tối đa một lần, có thể không chọn gì) có tổng đúng bằng S, ngược lại NO.
Ví dụ:
| Nhập | In ra |
|---|---|
5 93 34 4 12 5 | YES |
Kiểm tra: nhập
6 307 11 13 2 5 19Bài 6. Đếm tập con có tổng S
Phần tiêu đề “Bài 6. Đếm tập con có tổng S”Dòng đầu có n, S (1 ≤ n ≤ 100, 0 ≤ S ≤ 105), dòng thứ hai có n số nguyên dương (ai ≤ 105). In ra số tập con (kể cả tập rỗng nếu S = 0; hai tập khác nhau nếu khác vị trí) có tổng đúng bằng S, chia lấy dư 109 + 7.
Ví dụ:
| Nhập | In ra |
|---|---|
4 51 2 3 2 | 3 |
Kiểm tra: nhập
8 101 2 3 4 5 1 2 3Bài 7. Chia hai phần gần bằng nhau
Phần tiêu đề “Bài 7. Chia hai phần gần bằng nhau”Dòng đầu có n (1 ≤ n ≤ 100), dòng thứ hai có n số nguyên dương (ai ≤ 1000). Chia các số thành hai nhóm (nhóm có thể rỗng). In ra chênh lệch nhỏ nhất giữa tổng hai nhóm.
Ví dụ:
| Nhập | In ra |
|---|---|
41 6 11 5 | 1 |
Kiểm tra: nhập
831 26 33 21 40 25 18 9Bài 8. Đặt dấu cộng trừ
Phần tiêu đề “Bài 8. Đặt dấu cộng trừ”Dòng đầu có n, S (1 ≤ n ≤ 100, |S| ≤ 105), dòng thứ hai có n số nguyên dương (ai ≤ 1000). Đặt dấu + hoặc - trước mỗi số. In ra số cách đặt dấu để tổng bằng S, chia lấy dư 109 + 7.
Ví dụ:
| Nhập | In ra |
|---|---|
5 31 1 1 1 1 | 5 |
Kiểm tra: nhập
8 52 3 1 4 2 5 1 3Bài 9. Có bao nhiêu tổng khác nhau
Phần tiêu đề “Bài 9. Có bao nhiêu tổng khác nhau”Dòng đầu có n (1 ≤ n ≤ 100), dòng thứ hai có n số nguyên dương (ai ≤ 1000). In ra số giá trị khác nhau mà tổng của một tập con (kể cả tập rỗng, tổng 0) có thể nhận.
Ví dụ:
| Nhập | In ra |
|---|---|
31 2 4 | 8 |
Kiểm tra: nhập
63 3 5 10 10 20Nhóm 3. Đổi tiền
Phần tiêu đề “Nhóm 3. Đổi tiền”Bài 10. Số cách đổi tiền
Phần tiêu đề “Bài 10. Số cách đổi tiền”Có n loại tiền, mệnh giá a1, …, an (khác nhau), mỗi loại có không giới hạn tờ. Dòng đầu có n, S (1 ≤ n ≤ 100, 0 ≤ S ≤ 105), dòng thứ hai có n mệnh giá (1 ≤ ai ≤ 105). In ra số cách tạo ra đúng S (không kể thứ tự các tờ: 1 + 2 và 2 + 1 là một cách), chia lấy dư 109 + 7.
Ví dụ:
| Nhập | In ra |
|---|---|
3 51 2 5 | 4 |
Kiểm tra: nhập
4 302 5 3 6Bài 11. Ít tờ tiền nhất
Phần tiêu đề “Bài 11. Ít tờ tiền nhất”Dòng đầu có n, S (1 ≤ n ≤ 100, 0 ≤ S ≤ 105), dòng thứ hai có n mệnh giá (1 ≤ ai ≤ 105), mỗi loại không giới hạn tờ. In ra số tờ ít nhất để tạo đúng S, hoặc -1 nếu không được.
Ví dụ:
| Nhập | In ra |
|---|---|
3 111 2 5 | 3 |
Kiểm tra: nhập
3 301 10 25Bài 12. Leo cầu thang
Phần tiêu đề “Bài 12. Leo cầu thang”Cầu thang có S bậc. Mỗi bước được leo a1, a2, … hoặc an bậc (các số khác nhau). Dòng đầu có n, S (1 ≤ n ≤ 100, 0 ≤ S ≤ 105), dòng thứ hai có n số ai (1 ≤ ai ≤ 105). In ra số cách leo đúng S bậc (thứ tự các bước có quan trọng: 1 + 2 và 2 + 1 là hai cách), chia lấy dư 109 + 7.
Ví dụ:
| Nhập | In ra |
|---|---|
2 41 2 | 5 |
Kiểm tra: nhập
3 201 3 5Bài 13. Ít tờ nhất, số tờ có hạn
Phần tiêu đề “Bài 13. Ít tờ nhất, số tờ có hạn”Có n loại tiền, loại thứ i mệnh giá ai và có ki tờ. Dòng đầu có n, S (1 ≤ n ≤ 50, 0 ≤ S ≤ 2 × 104), n dòng sau mỗi dòng có a, k (1 ≤ a ≤ 2 × 104, 1 ≤ k ≤ 104). In ra số tờ ít nhất để tạo đúng S, hoặc -1.
Ví dụ:
| Nhập | In ra |
|---|---|
3 115 12 31 5 | 4 |
Kiểm tra: nhập
4 3710 27 33 41 2Nhóm 4. Biến thể
Phần tiêu đề “Nhóm 4. Biến thể”Bài 14. Túi rất lớn
Phần tiêu đề “Bài 14. Túi rất lớn”Dòng đầu có n, W (1 ≤ n ≤ 100, 1 ≤ W ≤ 109), n dòng sau mỗi dòng có w, v (1 ≤ w ≤ 109, 1 ≤ v ≤ 1000). Mỗi vật chọn tối đa một lần, tổng khối lượng không quá W. In ra tổng giá trị lớn nhất.
Ví dụ:
| Nhập | In ra |
|---|---|
3 83 304 505 60 | 90 |
Kiểm tra: nhập
6 1000000000300000000 70500000000 120200000000 40600000000 160100000000 15400000000 110Bài 15. Hai giới hạn: khối lượng và thể tích
Phần tiêu đề “Bài 15. Hai giới hạn: khối lượng và thể tích”Dòng đầu có n, W, V (1 ≤ n ≤ 100, 0 ≤ W, V ≤ 100), n dòng sau mỗi dòng có w, t, v: khối lượng, thể tích, giá trị một vật (1 ≤ w, t ≤ 100, 1 ≤ v ≤ 106). Mỗi vật chọn tối đa một lần, tổng khối lượng không quá W và tổng thể tích không quá V. In ra tổng giá trị lớn nhất.
Ví dụ:
| Nhập | In ra |
|---|---|
3 5 52 3 43 2 52 2 3 | 9 |
Kiểm tra: nhập
5 10 124 5 203 6 185 2 252 4 126 3 30Bài 16. Mỗi nhóm chọn một
Phần tiêu đề “Bài 16. Mỗi nhóm chọn một”Có g nhóm đồ vật, mỗi nhóm chọn nhiều nhất một vật. Dòng đầu có g, W (1 ≤ g ≤ 100, 0 ≤ W ≤ 104). Tiếp theo là g nhóm: dòng đầu của nhóm có số vật k (1 ≤ k ≤ 100), k dòng sau mỗi dòng có w, v (1 ≤ w ≤ 104, 1 ≤ v ≤ 106). In ra tổng giá trị lớn nhất với tổng khối lượng không quá W.
Ví dụ:
| Nhập | In ra |
|---|---|
2 723 54 722 35 9 | 10 |
Kiểm tra: nhập
3 1032 45 96 1023 67 1531 24 75 8Bài 17. Chọn đúng k vật
Phần tiêu đề “Bài 17. Chọn đúng k vật”Dòng đầu có n, k, W (1 ≤ k ≤ n ≤ 100, 0 ≤ W ≤ 1000), n dòng sau mỗi dòng có w, v (1 ≤ w ≤ 1000, 1 ≤ v ≤ 106). Chọn đúng k vật khác nhau có tổng khối lượng không quá W. In ra tổng giá trị lớn nhất, hoặc -1 nếu không chọn được.
Ví dụ:
| Nhập | In ra |
|---|---|
4 2 62 33 44 55 6 | 8 |
Kiểm tra: nhập
6 3 125 104 406 303 507 352 15