## Bài 1: [Tổng tập con 2](https://marisaoj.com/problem/329) - Chia mảng $A$ thành 2 phần: $A[1...\frac{n}{2}]$ và $A[\frac{n}{2} + 1...n]$. - Với mỗi phần, quay lui để sinh ra toàn bộ tổng các tập con. Ví dụ: $\{1, 2\}$ sinh ra được tập $\{0, 1, 2, 3\}$. Với nửa đầu gọi tập này là $S_1$, nửa sau gọi là $S_2$. Xét $x$ là một giá trị trong $S_1$, chỉ cần kiểm tra xem có tồn tại $k - x$ trong $S_2$ không. Code :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; #define int long long int n, k; int a[43]; void backtracking(int i, int target, int sum, unordered_set<int> &s){ if(i == target){ s.insert(sum); return; } backtracking(i + 1, target, sum, s); backtracking(i + 1, target, sum + a[i], s); } signed main(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> k; for(int i = 1; i <= n; i++) cin >> a[i]; unordered_set<int> s1, s2; backtracking(1, n / 2 + 1, 0, s1); backtracking(n / 2 + 1, n + 1, 0, s2); for(auto &x : s1){ if(s2.count(k - x)){ cout << "YES"; return 0; } } cout << "NO"; } ``` ::: ## Bài 2: [Robot](https://marisaoj.com/problem/113) - Tương tự bài trên, cũng chia các lệnh ra là hai tập nhỏ, thực hiện quay lui để sinh ra hai tập $S_1$, $S_2$ là vị trí của robot. Xét tọa độ $(x,y)$ trong $S_1$, cần kiểm tra xem có bao nhiêu lần $(a-x,b-y)$ xuất hiện trong $S_2$. Code :::spoiler ```c++ #include<bits/stdc++.h> #define int long long using namespace std; const int Sz = 50; const int mod = 1e9 + 7; void FastRead(){ ios_base::sync_with_stdio(false); cin.tie(NULL); } int n, a, b, x[Sz], y[Sz]; void bt(int i, int target, int sumx, int sumy, map<pair<int, int>, int> &m){ if (i == target){ m[{sumx, sumy}]++; return; } bt(i + 1, target, sumx, sumy, m); bt(i + 1, target, sumx + x[i], sumy + y[i], m); } signed main(){ FastRead(); cin >> n >> a >> b; for (int i = 1; i <= n; i++){ cin >> x[i] >> y[i]; } map<pair<int, int>, int> m1, m2; bt(1, n/2 + 1, 0, 0, m1); bt(n/2 + 1, n + 1, 0, 0, m2); int ans = 0; for (auto it : m1){ ans += m2[{a - it.first.first, b - it.first.second}] * it.second; } cout << ans; return 0; } ``` ::: ## Bài 3: [Chênh lệch nhỏ nhất](https://marisaoj.com/module/16) - Gọi $S$ là tổng tất cả các phần tử. Dễ thấy nếu chọn được một tập có tổng gần $\frac{S}{2}$ nhất thì chênh lệch giữa hai tập là nhỏ nhất. - Với mỗi giá trị $x$ trong $S_1$, chặt nhị phân để tìm giá trị gần $\frac{S}{2}-x$ nhất. Code: :::spoiler ``` #include<iostream> #include<vector> #include<algorithm> using namespace std; const long long INF=2e18+5; long long sum[5]; vector<int> t1,t2; vector<long long> ch1,ch2; void Btr(vector<int>& t,vector<long long>& ch,int i) { for (int j=0;j<2;j++) { sum[j]+=t[i]; if (i==int(t.size())-1) ch.push_back(abs(sum[0]-sum[1])); else Btr(t,ch,i+1); sum[j]-=t[i]; } } int main() { int n,a; cin>>n; for (int i=1;i<=int(n/2);i++) { cin>>a; t1.push_back(a); } Btr(t1,ch1,0); for (int i=n/2+1;i<=n;i++) { cin>>a; t2.push_back(a); } Btr(t2,ch2,0); sort(ch2.begin(),ch2.end()); long long res=INF; for (long long i:ch1) { int tmp=lower_bound(ch2.begin(),ch2.end(),i)-ch2.begin(); if (tmp==int(ch2.size())) { tmp--; res=min(res,i-ch2[tmp]); } else res=min(res,ch2[tmp]-i); if (tmp) res=min(res,i-ch2[tmp-1]); if (tmp<int(ch2.size())-1) res=min(res,ch2[tmp+1]-i); } cout<<res; } ``` ::: # Gợi ý ## Bài 4: [Trộm sách](https://marisaoj.com/problem/111) - Chia làm hai tập rồi quay lui. Với $(w, v)$ ở tập một, trong các tập con ở tập hai với cân nặng không vượt quá $S - w$, tìm giá trị $v$ lớn nhất để ghép đôi. ## Bài 5: [Tập con có tổng lớn nhất](https://marisaoj.com/problem/112) - Cũng chia làm hai tập rồi quay lui tổng mod $10^9$. Với $x$ ở tập $S_1$, tìm giá trị $y$ ở tập $S_2$ sao cho $x+y \mod 10^9$ lớn nhất, nghĩa là gần $x+y$ với $10^9$ nhất, nhưng không được lớn hơn hoặc bằng $10^9$, nếu mà lớn hơn thì lấy luôn $x$ hoặc $y$ cho rồi.