3. Một trong những ví dụ minh họa
3.4. Bài toán cái túi
Bài toán 3.4. Mang đến n dụng cụ (n ≤ 100), đồ vật thứ i tất cả trọng lượng là wi (wi ≤ 100) và có mức giá trị áp dụng là ci (ci ≤ 100). đề nghị xếp đồ vật vào một cái túi làm thế nào cho tổng giá chỉ trị thực hiện được xếp vào bên trong túi là to nhất. Biết rằng cái túi chỉ rất có thể mang được trọng lượng không vượt thừa b (b ≤ 100).
a) Phương pháp qui hoạch động
Bước 1. Nêu giảđịnh về hàm qui hoạch động
Gọi f(i, j) là cực hiếm sử dụng lớn số 1 của của những đồ thứ được xếp vào túi lúc chọn các đồ thứ 1, 2, …, i cùng trọng lượng số lượng giới hạn của túi là j. Lúc ấy giá trị nghiệm rất tốt của việc là f(n, m).
Bước 2:Tìm nghiệm của các bài toán con đơn giản và dễ dàng
Dễ thấy f(0, j) = 0 với đa số j = 1, 2, …, b
Bước 3: Xây dựng cách làm qui hoạch hễ
Với số lượng giới hạn trọng lượng j, việc chọn tối ưu trong những đồ trang bị 1, 2, …, i - 1, i để có giá trị lớn số 1 có nhị khả năng:
• còn nếu không chọn đồ vật thứ i thì f(i, j) là quý giá sử dụng lớn nhất có thể bằng cách chọn trong các đồ đồ dùng 1, 2, …, i - 1 với trọng lượng j, tức là
f(i, j) = f (i-1, j)
• Nếu tất cả chọn đồ vật i (với wi ≤ j) thì f(i, j) bởi giá trị áp dụng của đồ vật thứ i cộng với giá trị áp dụng lớn nhất rất có thể được bằng phương pháp chọn trong những các gói 1, 2, …, i - 1 với giới hạn trọng lượng là j - wi. Có thể nói rằng ta gồm công thức:
f(i, j) = ci + f(i-1, j - wi)
Kết thích hợp hai năng lực trên ta gồm công thức qui hoạch động: f(i, j) = max f(i-1, j) , ci + f(i-1, j - wi) cùng với i = 1, 2, …, n cùng j = 0, 1, 2, .., M.
Bước 4. Tìm kiếm lại nghiệm
Ta nhất quán hàm f(i,j) cùng với bảng F<1..n, 1..b> (còn gọi là bảng qui hoạch động). Sau khi tính xong xuôi mảng F, bài toán truy lốt trên F để tìm nghiệm như sau:
Chú ý rằng F
Nếu F
b) tế bào phỏng Pascal procedure Caitui; begin for j := 0 lớn b do F<0, j> := 0; for i := 1 lớn n vị for j := 1 lớn b vày begin F := F
if (j > wi) và (F i> + ci then F := F
end; end; procedure Trace; begin write(‘Max value: ‘, F
write(‘Chon bởi vat ‘, n, ‘ w = ‘, wn, ‘ value = ‘, vn); b := b - wn;
end; n := n - 1; end;
một trong những phần của tư liệu
I. Bài toán Cái túi và những bài toán áp dụng
1. Lời mở đầu
Bài toán Cái túi, Bài toán Xếp ba lô, Bài toán Knapsack,...là những tên gọi khác nhau mà chúng ta thường nghe đến, mà lại tất cả đều dùng để chỉ bình thường một bài toán tối ưu hóa tổ hợp, trong đó ta cần phải lựa chọn một số đồ vật để nhét vào một chiếc túi với tải trọng biết trước, sao cho tổng giá trị của các đồ vật nhét vào là lớn nhất có thể. Bài toán này đã được nghiên cứu trong rộng một thế kỷ, và nó là một vào những bài toán có ứng dụng vô cùng lớn lớn vào thực tế.
Bạn đang xem: Hướng dẫn giải bài toán cái túi

Có rất nhiều dạng khác nhau của bài toán cái túi mà tôi đã giới thiệu tới các bạn ở những chăm đề trước. Những dạng tiêu biểu của bài toán này có thể kể đến là:
Bài toán Knapsack với các giá trị số thực: Trọng lượng và giá trị của các món đồ là số thực. Bài toán này chỉ có thể giải quyết bằng phương pháp con quay lui (hoặc cải tiến bằng Nhánh cận).Bài toán Knapsack cho phép cắt nhỏ đồ vật (Fractional Knapsack): Các đồ vật được phép cắt ra và lấy một phần. Bài toán này có thể giải quyết bằng phương pháp Tham lam.Bài toán Knapsack 0−10 - 10−1: Các vật chỉ có thể chọn hoặc ko chọn, ngoài ra giá trị và trọng lượng của các vật đều là số nguyên. Bài toán này có thể giải bằng phương pháp Quy hoạch động.Trong bài viết này, chúng ta sẽ cùng tập trung nghiên cứu bài toán Knapsack 0−10 - 10−1 và một số ứng dụng của nó vào việc giải những bài toán Quy hoạch động có bốn duy tương tự.
2. Phát biểu bài toán
Cho nnn đồ vật khác nhau, đồ vật thứ iii có trọng lượng wiw_iwi và giá trị viv_ivi. Bạn có theo một chiếc túi có tải trọng tối nhiều là W,W,W, nhiệm vụ của bạn là chọn ra các đồ vật để đến vào túi làm thế nào để cho tổng giá trị của các đồ vật lấy được là lớn nhất có thể?
Input:
Dòng đầu tiên chứa hai số nguyên dương nnn và WWW.nnn dòng tiếp theo, mỗi dòng chứa nhị số nguyên dương wiw_iwi và viv_ivi phân tách nhau bởi dấu cách.Output:
Dòng đầu tiên in ra tổng giá trị lớn nhất của các vật lấy được.Dòng thứ nhị in ra chỉ số của các vật được lựa chọn theo thứ tự tăng dần.Sample Input:
3 5010 6020 10030 120Sample Output:
2202 3
3. Ý tưởng
Gọi dpXét đồ vật thứ i,i,i, và giới hạn trọng lượng hiện tại là j,j,j, ta có nhì phương án để lựa chọn:
Nếu vật thứ iii không được chọn vào phương án tối ưu, thì kết quả tối ưu sẽ được chọn vào (i−1)(i - 1)(i−1) đồ vật trước đó với giới hạn trọng lượng vẫn là jjj.Nếu vật thứ iii được chọn vào phương án tối ưu, thì tải trọng còn lại có thể sử dụng là (j−wi)(j - w_i)(j−wi) mang lại (i−1)(i - 1)(i−1) đồ vật phía trước, và ta được thêm giá trị viv_ivi của vật thứ iii. Dĩ nhiên, trường hợp này chỉ xét đến khi j≥wij ge w_ij≥wi.Dễ thấy rằng, nếu như jwij jwi thì chỉ có duy nhất cách lựa chọn đầu tiên, còn nếu j≥wij ge w_ij≥wi thì có thể lựa chọn theo cả hai cách để lấy cách tốt hơn. Tổng hợp lại, ta có công thức quy hoạch động:
dp
Cơ sở quy hoạch động dễ thấy là dp<0>
Để tầm nã vết lại các món đồ được chọn, ta sẽ đi ngược về bên trên bảng phương án, bắt đầu từ vị trí kết quả là dp
4. Cài đặt
Ngôn ngữ C++:
#include using namespace std;void enter(int &n, int &W, vector pair int, int > > items) cin >> n >> W; items.resize(n + 1); // Sử dụng kiểu pair để nhập dữ liệu các món đồ. // items.first và items.second lần lượt là trọng lượng và giá trị của đồ vật thứ i. For (int i = 1; i n; ++i) cin >> items.first >> items.second;// truy nã vết các món đồ được chọn.void trace_back(int n, int W, vector vector int > > &dp, vector pair int, int > > &items) vector int > picked_items; while (n > 0) if (dp
def enter(n, W, items): n, W = map(int, input().split()) items = <(0, 0) for _ in range(n + 1)> for i in range(1, n + 1): items = map(int, input().split())def trace_back(n, W, dp, items): picked_items = <> while n > 0: if dp1. Bài toán Cái túi vô hạn
Đề bài
Một tên trộm mang theo một chiếc túi có tải trọng WWW vào vào một tiệm đá quý. Vào tiệm có nnn loại đá quý, loại thứ iii có khối lượng wiw_iwi và giá trị vi,v_i,vi, với số lượng viên đá mỗi loại là không giới hạn. Thương hiệu trộm cần chọn ra một số viên đá quý để sở hữu theo thế nào cho tổng giá trị của chúng là lớn nhất có thể.
Xác định tổng giá trị đá quý lớn nhất mà tên trộm có thể mang theo với chiếc túi tải trọng W?
W?
W?
Input:
Dòng đầu tiên chứa hai số nguyên dương nnn và W (1≤n,W≤1000)W (1 le n, W le 1000)W (1≤n,W≤1000).nnn dòng tiếp theo, mỗi dòng chứa hai số nguyên dương wiw_iwi và viv_ivi phân tách nhau bởi dấu cách (1≤wi,vi≤100;∀i:1≤i≤n)(1 le w_i, v_i le 100; forall i: 1 le i le n)(1≤wi,vi≤100;∀i:1≤i≤n).Output:
Dòng đầu tiên in ra tổng giá trị lớn nhất của các viên đá quý mà tên trộm lấy được.Dòng thứ nhì in ra nnn số nguyên c1,c2,…,cnc_1, c_2, dots, c_nc1,c2,…,cn với cic_ici là số lần được chọn của viên đá quý thứ iii.Sample Input:
2 1001 150 30Sample Output:
100100 0Explanation:
Có rất nhiều cách để thương hiệu trộm sở hữu theo các viên đá quý:
Mang theo 222 viên có khối lượng 50,50,50, tổng giá trị là 606060.Mang theo 100100100 viên có khối lượng 1,1,1, tổng giá trị là 100100100.Mang theo 111 viên có khối lượng 505050 và 505050 viên có khối lượng 1,1,1, tổng giá trị là 808080.Xem thêm: Ga vé tàu quảng ngãi đi đà nẵng, giá rẻ thống nhất bắc nam
Vậy ta chọn phương án thứ 222.
Ý tưởng
Khác với bài toán cái túi bản gốc, ở bài toán này chúng ta được phép chọn một đồ vật nhiều lần. Nghĩa là ở mọi thời điểm lựa chọn, tất cả các đồ vật đều có thể được sử dụng.
Vì thế, ta chỉ cần sử dụng mảng một chiều dpdpdp với ý nghĩa là tổng giá trị đá quý lớn nhất thu được với giới hạn tải trọng là iii rứa vì mảng nhị chiều như bài toán gốc. Xét giới hạn tải trọng iii và viên đá quý có khối lượng wj,w_j,wj, giá trị vjv_jvj. Nếu như i≥wji ge w_ji≥wj thì ta lại có thể lựa chọn viên đá này vào tập các viên đá được chọn. Từ đó hình thành công thức:
dp=max(dp+vj);∀j:1≤j≤n and i≥wjdp = extmaxig(dp + v_jig); forall j: 1 le j le n ext và i ge w_jdp=max(dp+vj);∀j:1≤j≤n and i≥wj
Kết quả cuối cùng tất nhiên là dp
Để truy vết, ta sử dụng một mảng chosen_times extchosen\_timeschosen_times là số lần được chọn của viên đá quý thứ iii. Bắt đầu từ vị trí dp
Ta sẽ thu được giải thuật có độ phức tạp O(W×n)O(W imes n)O(W×n).
Cài đặt
Ngôn ngữ C++:
#include using namespace std;void enter(int &n, int &W, vector pair int, int > > gems) cin >> n >> W; gems.resize(n + 1); for (int i = 1; i n; ++i) cin >> gems.first >> gems.second;void trace_back(int n, int W, vector int > &dp, vector pair int, int > > &gems) vector int > chosen_times(n + 1, 0); while (W != 0) for (int i = 1; i n; ++i) if (dp
def enter(n, W, gems): n, W = map(int, input().split()) gems = <(0, 0) for i in range(n + 1)> for i in range(1, n + 1): gems = map(int, input().split())def trace_back(n, W, dp, gems): chosen_times = <0 for i in range(n + 1)> while W != 0: for i in range(1, n + 1): if dp2. Dãy con tổng bằng K
Đề bài
Cho một dãy số AAA gồm nnn phần tử nguyên a1,a2,…,ana_1, a_2, dots, a_na1,a2,…,an và một số nguyên KKK. Hãy đếm số dãy bé không liên tiếp của dãy AAA có tổng đúng bằng K?
K?
K?
Input:
Dòng đầu tiên chứa nhị số nguyên nnn và K (1≤n≤1000;1≤K≤1000)K (1 le n le 1000; 1 le K le 1000)K (1≤n≤1000;1≤K≤1000).Dòng thứ hai chứa nnn số nguyên a1,a2,…,ana_1, a_2, dots, a_na1,a2,…,an phân tách nhau bởi dấu cách (1≤ai≤1000;∀i:1≤i≤n)(1 le a_i le 1000; forall i: 1 le i le n)(1≤ai≤1000;∀i:1≤i≤n).Output:
Số nguyên duy nhất là số lượng dãy bé có tổng bằng KKK. Kết quả có thể rất lớn, đề nghị hãy in ra phần dư của kết quả sau thời điểm chia mang lại 109+710^9 + 7109+7.Sample Input:
4 61 2 3 3Sample Output:
3
Ý tưởng
Dễ nhận thấy, có tổng cộng 2n2^n2n dãy con, đề nghị phương pháp xoay lui duyệt mọi dãy nhỏ là hoàn toàn ko khả thi.Ý tưởng ở phía trên là sử dụng quy hoạch động với tứ duy giống như bài toán cái túi. Gọi dp
Vậy ta có công thức truy hỏi hồi:
dp
Kết quả cuối cùng sẽ là dp
Cơ sở quy hoạch động như sau: dp<0>=1,dp<0> = 1,dp<0>=1, vì luôn luôn có một dãy bé rỗng tổng bằng 000 chọn trong mảng bé A<1...i>A<1...i>A<1...i>. Tất nhiên, dp<0><0>dp<0><0>dp<0><0> cũng bằng 111.
Độ phức tạp giải thuật: O(n×K)O(n imes K)O(n×K).
Cài đặt
Ngôn ngữ C++:
#include using namespace std;const long long hack = 1e9 + 7;void enter(int &n, int &K, vector int > &a) cin >> n >> K; a.resize(n + 1); for (int i = 1; i n; ++i) cin >> a;void solution(int n, int K, vector int > &a) vector vector long long > > dp(n + 1, vector long long > (K + 1, 0)); for (int i = 0; i n; ++i) dp<0> = 1; for (int i = 1; i n; ++i) for (int j = 1; j K; ++j) dp
def solution(n, K, a): gian lận = 10 ** 9 + 7 dp = <<0> * (K + 1) for _ in range(n + 1)> for i in range(n + 1): dp<0> = 1 for i in range(1, n + 1): for j in range(1, K + 1): dp