低批
複習01背包
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 104> A;
array<int, 100004> dp;
bool bag(int n, int c){
for(int j = 0; j < n; j++){
for(int i = c; i >= A[j]; i--){
dp[i] = max(dp[i], dp[i - A[j]] + A[j]);
}
}
return dp[c] == c;
}
signed main(){
int m, n, sum;
cin >> m >> n;
while(m--){
sum = 0;
for(int &d : dp) d = 0;
for(int i = 0; i < n; i++){
cin >> A[i];
sum += A[i];
}
if(sum & 1) cout << "No\n";
else cout << (bag(n, sum / 2)? "Yes\n" : "No\n");
}
return 0;
}
無限背包
每個物品數量無限
狀態
dp(i, j) = i \ weight, first \ j \ items, max \ value
有限背包轉移式
dp(i, j) = max(dp(i, j - 1), dp(i - w_j, j - 1) + v_j)
無限背包轉移式
dp(i, j) = max(dp(i, j - 1), dp(i - w_j, j) + v_j)
複雜度?
狀態數 :
轉移時間 :
O(NC)
O(1)
O(NC)
扣的
array<int, 1004> W, V;
array<array<int, 1004>, 1004> dp;
int infbag(int n, int c){
int ans = 0;
for(int j = 0; j < n; j++){
for(int i = W[j]; i <= c; i++){
dp[i][j] = max(dp[i][j - 1], dp[i - W[j]][j] + V[j]);
ans = max(ans, dp[i][j]);
}
}
return ans;
}
記憶體比較小的扣的
array<int, 1004> W, V, dp;
int infbag(int n, int c){
int ans = 0;
for(int j = 0; j < n; j++){
for(int i = W[j]; i <= c; i++){
dp[i] = max(dp[i], dp[i - W[j]] + V[j]);
ans = max(ans, dp[i]);
}
}
return ans;
}
裸題
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 1004> A, D;
array<int, 10004> dp;
bool infbag(int w, int b, int n){
for(int j = 0; j < n; j++){
for(int i = A[j]; i < w; i++){
dp[i] = max(dp[i], dp[i - A[j]] + D[j]);
if(dp[i] >= b) return 1;
}
}
return 0;
}
signed main(){
int t, w, b, n, m;
cin >> t;
while(t--){
cin >> w >> b >> n;
for(int &d : dp) d = 0;
for(int i = 0; i < n; i++){
cin >> A[i] >> D[i];
}
cin >> m;
for(int i = 0; i < n; i++){
A[i] += m;
}
cout << (infbag(w, b, n)? "Yes\n" : "No\n");
}
return 0;
}
湊錢問題
有n種幣值,求湊出k元的最小數量
狀態
dp_i = min \ coin \ to \ i \ dollars
轉移
dp_i = min_{0 \le j < n}(dp_{i - v_j} + 1)
複雜度
狀態數 :
轉移時間 :
O(K)
O(N)
O(NK)
扣的
array<int, 1004> V, dp;
int money(int n, int k){
for(int i = 1; i <= k; i++){
dp[i] = 1e0;
for(int j = 0; j < n; j++){
if(V[j] < i) continue;
dp[i] = min(dp[i], dp[i - V[j]] + 1);
}
}
return dp[k];
}
不打馬賽克的裸題
扣的
#include <iostream>
#include <algorithm>
using namespace std;
int V[6] = {1, 5, 10, 12, 16, 20};
int dp[104];
int money(int k){
for(int i = 1; i <= k; i++){
dp[i] = 1e9;
for(int j = 0; j < 6; j++){
if(i - V[j] < 0) break;
dp[i] = min(dp[i], dp[i - V[j]] + 1);
}
}
return dp[k];
}
signed main(){
int n;
cin >> n;
cout << money(n);
return 0;
}
分數背包
物品可以被切割
哈哈其實是greedy啦
有限背包
每個物品都有限制數量
陽春之術
拆成01背包做
O(CNK)
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 54> W, M, C;
array<int, 10004> dp;
int bag(int n, int c){
int ans = 0;
for(int j = 0; j < n; j++){
for(int k = 0; k < C[j]; k++){
for(int i = c; i >= W[j]; i--){
dp[i] = max(dp[i], dp[i - W[j]] + M[j]);
ans = max(ans, dp[i]);
}
}
}
return ans;
}
signed main(){
int n, t;
cin >> n;
for(int i = 0; i < n; i++){
cin >> W[i] >> M[i] >> C[i];
}
cin >> t;
cout << bag(n, t);
return 0;
}
分塊之術
1, 2, 4, 8, 16 \dots, 2^{h - 1}, K - 2^h + 1 \ (2^h \le K)
O(CNlogK)
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 100004> W, M, C;
array<int, 1000004> dp;
int bag(int n, int c){
int ans = 0;
for(int j = 0; j < n; j++){
for(int k = 1; k <= C[j]; k *= 2){
for(int i = c; i >= k * W[j]; i--){
dp[i] = max(dp[i], dp[i - k * W[j]] + k * M[j]);
ans = max(ans, dp[i]);
}
C[j] -= k;
}
int k = C[j];
for(int i = c; i >= k * W[j]; i--){
dp[i] = max(dp[i], dp[i - k * W[j]] + k * M[j]);
ans = max(ans, dp[i]);
}
}
return ans;
}
signed main(){
int n, t;
cin >> n;
for(int i = 0; i < n; i++){
cin >> W[i] >> M[i] >> C[i];
}
cin >> t;
cout << bag(n, t);
return 0;
}
另一種湊錢問題
給你n個不同幣值的錢,每一種都有數量,問能不能湊出c元
對每種錢幣,從小於幣值的每個數開始,每次增加幣值,然後捨去超過數量的
狀態
dp(i, j) = i \ dollar, first \ j \ coins, can?
轉移
dp(i, j) = dp(i - v_jk_j, j - 1) | dp(i - v_j(k_j - 1), j - 1) |\\ \dots | dp(i, j - 1)
複雜度
狀態 :
轉移 :
O(NM)
O(1)
O(NM)
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 104> C, K;
array<array<int, 2>, 20004> dp;
int money(int n, int c){
for(int j = 0; j < n; j++){
for(int k = 0; k < C[j]; k++){
for(int i = k, l = k - C[j] * (K[j] + 1), sum = 0; i <= c; i += C[j], l += C[j]){
if(l >= 0) sum -= dp[l][1 ^ (j & 1)];
sum += dp[i][1 ^ (j & 1)];
dp[i][j & 1] = !!sum;
}
}
}
return dp[c][1 ^ (n & 1)];
}
signed main(){
int t, n, m;
cin >> t;
while(t--){
for(int i = 0; i <= m; i++){
for(int j : {0, 1}){
dp[i][j] = 0;
}
}
dp[0][0] = dp[0][1] = 1;
cin >> n >> m;
for(int i = 0; i < n; i++){
cin >> C[i] >> K[i];
}
cout << (money(n, m)? "Yes\n" : "No\n");
}
return 0;
}
LIS
Longest Increasing Subsequence
狀態
dp_i = LIS \ while \ choosing \ A_i \ as \ last \ one
轉移
dp_i = max_{j < i, A_j < A_i}(dp_j + 1)
複雜度
狀態數 :
轉移時間 :
O(N)
O(N)
O(N^2)
扣的
array<int, 1004> dp, A;
int LIS(int n){
int ans = 0;
for(int i = 0; i < n; i++){
dp[i] = 1;
for(int j = 0; j < i; j++){
if(A[i] > A[j]){
dp[i] = max(dp[i], dp[j] + 1);
}
}
ans = max(ans, dp[i]);
}
return ans;
}
優化
找到目前LIS中第一個比自己大的,然後取代他
LIS為遞增序列,因此可以二分搜
複雜度
搜尋時間 * 操作次數
O(NlogN)
扣的
#define pb push_back
vector<int> lis;
array<int, 100004> A;
int LIS(int n){
for(int i = 0; i < n; i++){
auto p = lower_bound(lis.begin(), lis.end(), A[i]);
if(p == lis.end()) lis.pb(A[i]);
else *p = A[i];
}
return lis.size();
}
題目
扣的
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
vector<int> lis;
array<int, 100004> A;
int LIS(int n){
for(int i = 0; i < n; i++){
auto p = lower_bound(lis.begin(), lis.end(), A[i]);
if(p == lis.end()) lis.pb(A[i]);
else *p = A[i];
}
return lis.size();
}
signed main(){
int n;
cin >> n;
for(int i = 0; i < n; i++){
cin >> A[i];
}
cout << LIS(n);
return 0;
}
扣的
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
struct doll{
int h, w;
};
array<doll, 20004> D;
vector<int> lis;
bool cmp(doll a, doll b){
if(a.h != b.h) return a.h < b.h;
return a.w > b.w;
}
int LIS(int n){
lis.clear();
for(int i = 0; i < n; i++){
auto p = lower_bound(lis.begin(), lis.end(), D[i].w);
if(p == lis.end()) lis.pb(D[i].w);
else *p = D[i].w;
}
return lis.size();
}
signed main(){
int t, n;
cin >> t;
while(t--){
cin >> n;
for(int i = 0; i < n; i++){
cin >> D[i].h >> D[i].w;
}
sort(D.begin(), D.begin() + n, cmp);
cout << LIS(n) << "\n";
}
return 0;
}
扣的
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
struct dot{
int x, y;
};
array<dot, 200004> D;
vector<int> lis;
bool cmp(dot a, dot b){
if(a.x != b.x) return a.x < b.x;
return a.y < b.y;
}
int LIS(int n){
lis.clear();
for(int i = 0; i < n; i++){
auto p = upper_bound(lis.begin(), lis.end(), D[i].y);
if(p == lis.end()) lis.pb(D[i].y);
else *p = D[i].y;
}
return lis.size();
}
signed main(){
int n;
cin >> n;
for(int i = 0; i < n; i++){
cin >> D[i].x >> D[i].y;
}
sort(D.begin(), D.begin() + n, cmp);
cout << LIS(n);
return 0;
}
扣的
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
map<int, int> P;
array<int, 200004> A, B, D, L, lis;
array<vector<int>, 200004> S;
int lowerbound(int x){
int p = 0;
for(int i = 17; i >= 0; i--){
if(p + (1 << i) < lis.size() && lis[p + (1 << i)] > x){
p += (1 << i);
}
}
return p + 1;
}
void LIS(int n){
for(int i = n; i > 0; i--){
int p = lowerbound(D[i]);
lis[p] = D[i];
L[i] = p;
}
}
void print(int n, int now){
if(!n) return;
int nxt;
for(int s : S[n]){
if(s < now) break;
if(D[s] > D[now] && A[s] > A[nxt]) nxt = s;
}
cout << A[nxt] << " ";
print(n - 1, nxt);
}
signed main(){
int n, mx = 0;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> A[i];
P[A[i]] = i;
}
for(int i = 1; i <= n; i++){
cin >> B[i];
D[P[B[i]]] = i;
}
LIS(n);
for(int i = n; i > 0; i--){
S[L[i]].pb(i);
mx = max(mx, L[i]);
}
print(mx, 0);
return 0;
}
編輯距離
送你兩個字串,有三種操作 : 刪掉一個字,加入一個字,更改一個字。求把兩個字串變一樣的最少操作次數
狀態
dp(i, j) = min \ operations \ when \\ first \ i \ char \ on \ first \\ string \ and \ first \ j \\ char \ on \ second \ string
轉移
dp(i, j) = dp(i - 1, j - 1), if(S_{1i} == S_{2j})\\
dp(i, j) = min(dp(i, j - 1), dp(i - 1. j), dp(i - 1, j - 1)) + 1
複雜度
狀態數 :
轉移時間 :
O(S_1sizeS_2size)
O(1)
O(S_1sizeS_2size)
題目
扣的
#include <bits/stdc++.h>
using namespace std;
string A, B;
array<array<int, 1004>, 1004> dp;
int ED(int i, int j){
if(i == 0 || j == 0) return i? i : j;
if(dp[i][j]) return dp[i][j];
if(A[i - 1] == B[j - 1]) return dp[i][j] = ED(i - 1, j - 1);
return dp[i][j] = min({ED(i - 1, j), ED(i, j - 1), ED(i - 1, j - 1)}) + 1;
}
signed main(){
getline(cin, A);
getline(cin, B);
cout << ED(A.size(), B.size());
return 0;
}
位元低批
位元
二進位的位數
1 0 0 0 0
16
| |
位元運算
~ | not | 變反的 |
& | and | 和 |
| | or | 或 |
^ | xor | 相加不進位 |
<< | left shift | 左移 |
>> | right shift | 右移 |
位元枚舉
直接看題目
對每個位置枚舉0 1,
然後算答案
複雜度
狀態數 :
算答案 :
O(NK2^N)
O(2^N)
O(NK)
扣的
#include <bits/stdc++.h>
using namespace std;
array<array<int, 16>, 3> S;
void run(int n, int k){
array<int, 3> dif;
int now, ans, sml, big = 0;
for(int i = 0; i < 1 << n; i++){
sml= 1e9;
for(int l = 0; l < k; l++){
dif[l] = 0;
}
for(int j = 0; j < n; j++){
now = (i >> j) & 1;
for(int l = 0; l < k; l++){
if(S[l][j] != now) dif[l]++;
}
}
for(int l = 0; l < k; l++){
sml = min(sml, dif[l]);
}
if(sml > big){
big = sml;
ans = i;
}
}
for(int j = 0; j < n; j++){
cout << ((ans >> j) & 1);
}
}
signed main(){
int n, k;
char c;
cin >> n >> k;
if(n > 15) return 0;
for(int i = 0; i < k; i++){
for(int j = 0; j < n; j++){
cin >> c;
S[i][j] = (c ^ '0');
}
}
run(n, k);
return 0;
}
好土低批
直接看題目
狀態
dp(i, j) = min \ time \ to \ finish \ at \ j \ in\ set \ i
轉移
dp(i, j) = min_{k \subset i}(dp(i - 2^j, k) + wait(k, j))
複雜度
狀態 :
轉移 :
O(N2^N)
O(N)
O(N^22^N)
扣的
#include <bits/stdc++.h>
using namespace std;
array<array<int, 16>, 1 << 16> dp;
array<int, 16> T;
int up(int t, int x){
if(t % x == 0) return t;
return x * (t / x + 1);
}
int cost(int t, int i, int j){
t += abs(j - i);
t = up(t, T[j]);
return t;
}
int DP(int n){
int ans = 1e9;
for(int i = 1; i < 1 << n; i++){
for(int j = 0; j < n; j++){
if(!(i & (1 << j))) continue;
if(i ^ (1 << j)) dp[i][j] = 1e9;
for(int k = 0; k < n; k++){
if(k == j || !(i & (1 << k))) continue;
dp[i][j] = min(dp[i][j], cost(dp[i ^ (1 << j)][k], k, j));
}
}
}
for(int i = 0; i < n; i++){
ans = min(ans, dp[(1 << n) - 1][i]);
}
return ans;
}
signed main(){
int n;
cin >> n;
for(int i = 0; i < n; i++){
cin >> T[i];
dp[1 << i][i] = T[i];
}
cout << DP(n);
return 0;
}
題目
扣的
#include <bits/stdc++.h>
using namespace std;
array<array<int, 15>, 15> A, V;
array<int, 1 << 15> dp;
int DP(int n){
int ans = 0, t;
for(int s = (1 << n) - 1; s >= 0; s--){
for(int i = 0; i < n; i++){
if(!(s & (1 << i))) continue;
for(int j = i + 1; j < n; j++){
if(!(s & (1 << j))) continue;
t = s ^ (1 << i) ^ (1 << j) ^ (1 << A[i][j]);
dp[t] = max(dp[t], dp[s] + V[i][j]);
}
}
}
for(int i = 1; i < (1 << n); i <<= 1){
ans = max(ans, dp[i]);
}
return ans;
}
signed main(){
int n;
while(cin >> n){
for(int &d : dp) d = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cin >> V[i][j];
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cin >> A[i][j];
}
}
cout << DP(n) << "\n";
}
return 0;
}
本提極其考驗壓常技巧
扣的
#include <bits/stdc++.h>
using namespace std;
int n;
array<int, 1 << 21> dp;
array<array<int, 21>, 21> R;
int lownotbit(int x){
for(int i = 0; i < n; i++){
if(!(x & (1 << i))) return i;
}
}
int DP(){
int i, t;
for(int s = 0; s < (1 << n); s++){
if(dp[s] == 1e9) continue;
i = lownotbit(s);
for(int j = i + 1; j < n; j++){
if(s & (1 << j)) continue;
for(int k = j + 1; k < n; k++){
if(s & (1 << k)) continue;
t = s | (1 << i) | (1 << j) | (1 << k);
dp[t] = min(dp[t], dp[s] + R[i][j] + R[j][k] + R[i][k]);
}
}
}
return dp[(1 << n) - 1];
}
signed main(){
int t;
cin >> t;
while(t--){
cin >> n;
for(int i = 1; i < (1 << n); i++) dp[i] = 1e9;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cin >> R[i][j];
}
}
cout << DP() << "\n";
}
return 0;
}
扣的
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
int n;
array<double, 24> X, Y;
array<int, (1 << 24)> dp;
array<array<int, 24>, 24> line;
int highbit(int x){
int p = 0;
for(int i = 4; i >= 0; i--){
if(((1 << (p + (1 << i)))) <= x) p += (1 << i);
}
return p;
}
int DP(int n){
int k;
for(int i = 1; i < 1 << n; i++){
dp[i] = n;
k = highbit(i);
for(int j = 0; j < n; j++){
dp[i] = min(dp[i], dp[i & ~line[k][j]] + 1);
}
}
return dp[(1 << n) - 1];
}
signed main(){
int t;
double a, b;
cin >> t;
while(t--){
cin >> n;
for(int i = 0; i < n; i++){
cin >> X[i] >> Y[i];
}
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
line[i][j] = (1 << i);
if(X[i] == X[j]) continue;
/*
{yi = axi^2 + bxi ----- 1
{yj = axj^2 + bxj ----- 2
a :
1 - 2 :
yj(xi / xj) = axjxi + bxi
yi - yj(xi / xj) = axi^2 - axjxi
yi - yj(xi / xj) = a(xi^2 - xixj)
a = (yi - yj(xi / xj)) / (xi^2 - xixj)
a = (yixj - yjxi) / xixj(xi-xj)
b :
yi / xi = axi + b
b = yi / xi - axi
*/
a = (Y[i] * X[j] - Y[j] * X[i]) / (X[i] * X[j] * (X[i] - X[j]));
b = (Y[i] / X[i]) - (a * X[i]);
if(a >= 0) continue;
for(int k = 0; k < n; k++){
if(Y[k] - (a * X[k] * X[k] + b * X[k]) < eps && Y[k] - (a * X[k] * X[k] + b * X[k]) > -eps){
line[i][j] |= (1 << k);
}
}
}
}
cout << DP(n) << "\n";
}
return 0;
}
大背包
W那麼大,怎麼辦
把背包反過來做啊
dp(i, j) = i \ value, first \ j \ items, min \ weight
狀態
dp(i, j) = min(dp(i, j - 1), dp(i - v_j, j - 1) + w_j)
轉移
複雜度
狀態 :
轉移 :
O(N\sum V)
O(1)
O(N\sum V)
扣的
#include <bits/stdc++.h>
using namespace std;
array<int, 104> W, V;
array<int, 100004> dp;
int bag(int n, int c){
int ans = 0;
for(int j = 0; j < n; j++){
for(int i = 100000; i >= V[j]; i--){
dp[i] = min(dp[i], dp[i - V[j]] + W[j]);
if(dp[i] <= c) ans = max(ans, i);
}
}
return ans;
}
signed main(){
int n, w;
cin >> n >> w;
for(int &d : dp) d = w + 1;
dp[0] = 0;
for(int i = 0; i < n; i++){
cin >> W[i] >> V[i];
}
cout << bag(n, w);
return 0;
}
T跟A都那麼大,怎麼辦
跟上一題一樣?
這題是問最大耶
切兩半位元枚舉
最後再二分搜
N \le 40 !!!!
複雜度
狀態數 :
算答案 :
排序 :
二分搜 :
O(2^N)
O(N)
O(N2^N)
O(N2^N)
O(N2^N)
扣的
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
array<int, 44> A;
vector<int> bag(int l, int r){
vector<int> V;
for(int i = 0; i < (1 << (r - l + 1)); i++){
V.pb(0);
for(int j = 0; j <= r - l; j++){
if(i & (1 << j)){
V[i] += A[l + j];
}
}
}
return V;
}
int BIS(vector<int> L, vector<int> R, int c){
int ans = 0;
for(int l : L){
if(c - l < R[0]) continue;
ans = max(ans, l + *--upper_bound(R.begin(), R.end(), c - l));
}
return ans;
}
signed main(){
int n, t;
vector<int> L, R;
cin >> n >> t;
for(int i = 0; i < n; i++){
cin >> A[i];
}
L = bag(0, n / 2 - 1);
R = bag(n / 2, n - 1);
sort(L.begin(), L.end());
sort(R.begin(), R.end());
cout << BIS(L, R, t);
return 0;
}
Copy of 低批
By yennnn
Copy of 低批
- 271