Professional Documents
Culture Documents
SOL DP Bitmask Bai123
SOL DP Bitmask Bai123
SOL DP Bitmask Bai123
1
Bài tập
01 QBSELECT
02 VKNIGHTS
03 COWGIRL
2
QBSELECT
Tóm tắt
Cho một ma trận 4 * n, chọn một số ô
sao cho tổng giá trị là lớn nhất, và
không có ô nào được chọn kề nhau
3
QBSELECT
int trau(int i) {
if (i > n) return 0;
// không chọn i, -> qua i + 1
int r1 = trau(i + 1);
//chọn i, bỏ qua i + 1 -> i + 2
int r2 = trau(i + 2) + a[i];
return max(r1, r2);
}
//trau(1)
4
QBSELECT
5
QBSELECT
6
QBSELECT
7
QBSELECT
int trau(int i, int pre) {
if (i > n) return 0;
int res = -infinity;
for (int nxt = 0; nxt <= 1; ++nxt)
if (nxt + pre != 2) {
int cost = 0;
if (nxt == 1) cost = a[i];
res = max(res, trau(i + 1, nxt) + cost);
}
return res;
}
int trau(int i, int pre) {
if (i > n) return;
int res = -infinity;
for (int nxt = 0; nxt <= 1; ++nxt)
if (nxt + pre != 2)
res = max(res, trau(i + 1, nxt) + nxt * a[i]);
return res;
}
8
QBSELECT
9
QBSELECT
int trau(int i, int x, int y, int z) {
if (i > n) return 0;
int res = -infinity;
for (int xx = 0; xx <= 1; ++xx)
for (int yy = 0; yy <= 1; ++yy)
for (int zz = 0; zz <= 1; ++zz)
if (x + xx != 2 && y + yy != 2 && z + zz != 2
&& xx + yy != 2 && yy + zz != 2)
res = max(res,
trau(i + 1, xx, yy, zz)
+ xx * a[i][1]
+ yy * a[i][2]
+ zz * a[i][3]
);
return res;
}
10
QBSELECT
11
QBSELECT
12
QBSELECT
Độ phức tạp?
(Giả sử đã thêm nhớ)
int trau(int i, int x, int y, int z) {
if (i > n) return 0; Số lượng trạng thái: 𝑂(𝑛 ∗ 2 )
int res = -infinity;
for (int xx = 0; xx <= 1; ++xx) Chi phí chuyển: 𝑂(2 )
for (int yy = 0; yy <= 1; ++yy)
for (int zz = 0; zz <= 1; ++zz)
if (x + xx != 2 && y + yy != 2 && z + zz != 2
&& xx + yy != 2 && yy + zz != 2)
res = max(res,
trau(i + 1, xx, yy, zz)
+ xx * a[i][1]
+ yy * a[i][2]
+ zz * a[i][3]
);
return res;
} 13
QBSELECT
int trau(int i, int x, int y, int z) {
if (i > n) return 0;
int res = -infinity;
for (int xx = 0; xx <= 1; ++xx)
for (int yy = 0; yy <= 1; ++yy)
for (int zz = 0; zz <= 1; ++zz)
if (x + xx != 2 && y + yy != 2 && z + zz != 2
&& xx + yy != 2 && yy + zz != 2)
res = max(res,
trau(i + 1, xx, yy, zz)
+ xx * a[i][1]
+ yy * a[i][2]
+ zz * a[i][3]
);
return res;
}
14
QBSELECT
int trau(int i, int x, int y, int z) {
if (i > n) return 0;
int res = -infinity;
for (int mask = 0; mask < 8; ++mask) {
int xx = getBit(mask, 0);
int yy = getBit(mask, 1);
int zz = getBit(mask, 2);
if (x + xx != 2 && y + yy != 2 && z + zz != 2
&& xx + yy != 2 && yy + zz != 2)
res = max(res,
trau(i + 1, xx, yy, zz)
+ xx * a[i][1]
+ yy * a[i][2]
+ zz * a[i][3]
);
}
return res;
}
15
QBSELECT
int trau(int i, int pre_mask) {
if (i > n) return 0;
Độ phức tạp?
int x = getBit(pre_mask, 0);
(Giả sử đã thêm nhớ)
int y = getBit(pre_mask, 1);
int z = getBit(pre_mask, 2);
int res = -infinity; Số lượng trạng thái: 𝑂(𝑛 ∗ 2 )
for (int mask = 0; mask < 8; ++mask) {
int xx = getBit(mask, 0); Chi phí chuyển: 𝑂(2 )
int yy = getBit(mask, 1);
int zz = getBit(mask, 2);
if (x + xx != 2 && y + yy != 2 && z + zz != 2
&& xx + yy != 2 && yy + zz != 2)
res = max(res,
trau(i + 1, mask)
+ xx * a[i][1]
+ yy * a[i][2]
+ zz * a[i][3]
);
}
return res;
16
}
QBSELECT
x + xx != 2 && y + yy != 2 && z + zz != 2
17
QBSELECT
int trau(int i, int pre_mask) {
if (i > n) return 0;
int res = -infinity;
for (int mask = 0; mask < 8; ++mask) {
int xx = getBit(mask, 0);
int yy = getBit(mask, 1);
int zz = getBit(mask, 2);
if ((pre_mask & mask) == 0
&& xx + yy != 2 && yy + zz != 2)
res = max(res,
trau(i + 1, mask)
+ xx * a[i][1]
+ yy * a[i][2]
+ zz * a[i][3]
);
}
return res;
}
18
QBSELECT
&& xx + yy != 2 && yy + zz != 2
19
QBSELECT
&& xx + yy != 2 && yy + zz != 2
20
QBSELECT
int trau(int i, int pre_mask) {
if (i > n) return 0;
Độ phức tạp?
int &res = f[i][pre_mask];
if (res != -1)
return res;
Số lượng trạng thái: 𝑂(𝑛 ∗ 2 )
res = -infinity;
for (int mask = 0; mask < 8; ++mask) {
Chi phí chuyển: 𝑂(2 )
int xx = getBit(mask, 0);
int yy = getBit(mask, 1);
int zz = getBit(mask, 2);
if ((pre_mask & mask) == 0 && (mask & (mask << 1) == 0))
res = max(res,
trau(i + 1, mask)
+ xx * a[i][1]
+ yy * a[i][2]
+ zz * a[i][3]
);
}
return res;
} 21
QBSELECT
for (int i = 1; i <= n; ++i)
for (int mask = 0; mask < 8; ++mask) {
f[i][mask] = -infinity;
for (int pre_mask = 0; pre_mask < 8; ++pre_mask)
if ((pre_mask & mask) == 0 && (mask & (mask << 1) == 0)) {
int xx = getBit(mask, 0);
int yy = getBit(mask, 1);
int zz = getBit(mask, 2);
f[i][mask] = max(f[i][mask],
f[i - 1][pre_mask]
+ xx * a[i][1]
+ yy * a[i][2]
+ zz * a[i][3]
);
}
}
}
22
Q&A
23
VKNIGHS
Tóm tắt
Cho một ma trận 3 * n có một số ô cấm,
tìm số lượng lớn nhất các quân mã có
thể đặt lên bàn cờ, sao cho không có 2
quân mã nào có thể ăn được nhau (sau
1 bước)
24
VKNIGHS
Tóm tắt
Các biến cần quan tâm Cho một ma trận 3 * n có một số ô cấm,
- Các quân mã đã được đặt (tập hợp) tìm số lượng lớn nhất các quân mã có
- Ô đang đứng thể đặt lên bàn cờ, sao cho không có 2
quân mã nào có thể ăn được nhau (sau
1 bước)
25
VKNIGHTS
Làm trâu
26
int calc(int i, set<pair<int, int> > placed) {
if (i > n) return 0; //đã điền xong cột cuối cùng
int res = 0;
//duyệt các cách điền cột i
for (int x = 0; x <= 1; ++x) {
if (x == 1 && invalid(placed, {1, i})) continue;
for (int y = 0; y <= 1; ++y) {
if (y == 1 && invalid(placed, {2, i})) continue;
for (int z = 0; z <= 1; ++z) {
if (z == 1 && invalid(placed, {3, i})) continue;
//tạo tập hợp mới
set<pair<int, int> > nxt_placed = placed;
if (x) nxt_placed.insert({1, i});
if (y) nxt_placed.insert({2, i});
if (z) nxt_placed.insert({3, i});
res = max(res, calc(i + 1, nxt_placed) + x + y + z);
}
}
} //calc(1, set rỗng)
return res;
}
27
Invalid
Làm sao xác định có thể thêm một con mã vào tập hợp hay
không?
28
Thêm nhớ ????
int calc(int i, set<pair<int, int> > placed) {
//...
}
30
Invalid
bool invalid(const set<pair<int, int> > &placed, pair<int, int> pos) {
if (block[pos.first][pos.second])
return true;
//nếu 2 con mã có khoảng cách là 3 thì bọn nó ăn nhau
for (auto knight : placed)
if (abs(knight.first - pos.first) + abs(knight.second - pos.second) == 3)
return true;
return false;
}
31
int calc(int i, set<pair<int, int> > placed) {
//...
//tạo tập hợp mới
set<pair<int, int> > nxt_placed = placed;
if (x) nxt_placed.insert({1, i});
if (y) nxt_placed.insert({2, i});
if (z) nxt_placed.insert({3, i});
//...
}
32
int calc(int i, set<pair<int, int> > placed) {
//...
//tạo tập hợp mới
set<pair<int, int> > nxt_placed;
for (auto knight : placed)
if (knight.second >= i - 1)
nxt_placed.insert(knight);
if (x) nxt_placed.insert({1, i});
if (y) nxt_placed.insert({2, i});
if (z) nxt_placed.insert({3, i});
//...
}
33
int calc(int i, set<pair<int, int> > placed) {
if (i > n)
return 0; //đã điền xong cột cuối cùng
if (f[i].count(placed) != 0)
return f[i][placed];
int res = 0;
Độ phức tạp?
//duyệt các cách điền cột i
for (int x = 0; x <= 1; ++x) {
if (x == 1 && invalid(placed, {1, i}))
continue;
for (int y = 0; y <= 1; ++y) {
Số lượng trạng thái:
if (y == 1 && invalid(placed, {2, i})) - i:
continue;
for (int z = 0; z <= 1; ++z) {
- placed: ∗
37
int calc(int i, set<int> pre_col1, set<int> pre_col2) {
if (i > n)
return 0; //đã điền xong cột cuối cùng
int res = 0;
//duyệt các cách điền cột i
for (int mask = 0; mask < 8; ++mask) {
int x = getBit(mask, 0);
int y = getBit(mask, 1);
int z = getBit(mask, 2);
if (x == 1 && invalid(pre_col1, pre_col2, 1, i))
continue;
if (y == 1 && invalid(pre_col1, pre_col2, 2, i))
continue;
if (z == 1 && invalid(pre_col1, pre_col2, 3, i))
continue;
//tạo tập hợp mới
set<int> nxt_placed;
if (x) nxt_placed.insert(1);
if (y) nxt_placed.insert(2);
if (z) nxt_placed.insert(3);
res = max(res, calc(i + 1, pre_col2, nxt_placed) + x + y + z);
}
return res; 38
}
Invalid
39
Invalid
bool invalid(const set<int> &pre_col1, const set<int> &pre_col2, int row, int col) {
if (block[row][col])
return true;
//nếu 2 con mã có khoảng cách là 3 thì bọn nó ăn nhau
for (int knight : pre_col1)
if (abs(knight - row) == 1)
return true;
for (int knight : pre_col2)
if (abs(knight - row) == 2)
return true;
return false;
}
40
int calc(int i, set<int> pre_col1, set<int> pre_col2) {
if (i > n)
return 0; //đã điền xong cột cuối cùng
int res = 0;
//duyệt các cách điền cột i
for (int mask = 0; mask < 8; ++mask) {
int x = getBit(mask, 0);
int y = getBit(mask, 1);
int z = getBit(mask, 2);
if (x == 1 && invalid(pre_col1, pre_col2, 1, i))
continue;
if (y == 1 && invalid(pre_col1, pre_col2, 2, i))
continue;
if (z == 1 && invalid(pre_col1, pre_col2, 3, i))
continue;
//tạo tập hợp mới
set<int> nxt_placed;
if (x) nxt_placed.insert(1);
if (y) nxt_placed.insert(2);
if (z) nxt_placed.insert(3);
res = max(res, calc(i + 1, pre_col2, nxt_placed) + x + y + z);
}
return res; 41
}
int calc(int i, int pre_col1_mask, int pre_col2_mask) {
if (i > n)
return 0; //đã điền xong cột cuối cùng
int res = 0;
//duyệt các cách điền cột i
for (int mask = 0; mask < 8; ++mask) {
int x = getBit(mask, 0);
int y = getBit(mask, 1);
int z = getBit(mask, 2);
if (x == 1 && invalid(pre_col1_mask, pre_col2_mask, 1, i))
continue;
if (y == 1 && invalid(pre_col1_mask, pre_col2_mask, 2, i))
continue;
if (z == 1 && invalid(pre_col1_mask, pre_col2_mask, 3, i))
continue;
res = max(res, calc(i + 1, pre_col2_mask, mask) + x + y + z);
}
return res;
}
42
int calc(int i, int pre_col1_mask, int pre_col2_mask) {
if (i > n)
return 0; //đã điền xong cột cuối cùng Độ phức tạp?
int res = 0;
//duyệt các cách điền cột i Số lượng trạng thái:
for (int mask = 0; mask < 8; ++mask) { - i:
int x = getBit(mask, 0);
- Mask1:
int y = getBit(mask, 1);
int z = getBit(mask, 2);
- Mask2:
if (x == 1 && invalid(pre_col1_mask, pre_col2_mask, 1, i)) Chi phí chuyển:
continue;
- Duyệt mask:
if (y == 1 && invalid(pre_col1_mask, pre_col2_mask, 2, i))
continue; - Kiểm tra:
if (z == 1 && invalid(pre_col1_mask, pre_col2_mask, 3, i)) - Hằng số bé
continue;
res = max(res, calc(i + 1, pre_col2_mask, mask) + x + y + z);
}
return res;
}
43
int calc(int i, int pre_col1_mask, int pre_col2_mask) {
if (i > n)
return 0; //đã điền xong cột cuối cùng
int res = 0;
//duyệt các cách điền cột i
for (int mask = 0; mask < 8; ++mask) {
int x = getBit(mask, 0);
int y = getBit(mask, 1);
int z = getBit(mask, 2);
if (x == 1 && invalid(pre_col1_mask, pre_col2_mask, 1, i))
continue;
if (y == 1 && invalid(pre_col1_mask, pre_col2_mask, 2, i))
continue;
if (z == 1 && invalid(pre_col1_mask, pre_col2_mask, 3, i))
continue;
res = max(res, calc(i + 1, pre_col2_mask, mask) + x + y + z);
}
return res;
}
44
int calc(int i, int pre_col1_mask, int pre_col2_mask) {
if (i > n)
return 0; //đã điền xong cột cuối cùng
int res = 0;
//duyệt các cách điền cột i
for (int mask = 0; mask < 8; ++mask) {
if (invalid(pre_col1_mask, pre_col2_mask, mask, i))
continue;
res = max(res, calc(i + 1, pre_col2_mask, mask) + cntBit(mask));
}
return res;
}
45
Invalid
bool invalid(int pre_col1, int pre_col2, int mask, int col) {
//kiểm tra block
for (int i = 1; i <= 3; ++i)
if (getBit(mask, i - 1) && block[i][col])
return true;
return false;
}
47
Độ phức tạp?
int calc(int i, int pre_col1_mask, int pre_col2_mask) {
if (i > n) Số lượng trạng thái:
return 0; //đã điền xong cột cuối cùng - i:
int &res = f[i][pre_col1_mask][pre_col2_mask]; - Mask1:
if (res != -1) return res; - Mask2:
res = 0;
//duyệt các cách điền cột i Chi phí chuyển:
for (int mask = 0; mask < 8; ++mask) { - Duyệt mask:
if (invalid(pre_col1_mask, pre_col2_mask, mask, i)) - Kiểm tra:
continue; - Hằng số bé
res = max(res, calc(i + 1, pre_col2_mask, mask) + cntBit(mask));
}
return res;
}
48
Q&A
49
COWGIRL
Tóm tắt
Cho một ma trận n*m, đếm số cách
điền 0 1 vào bảng sao cho không có
bảng con 2*2 nào có toàn số 1 hoặc
toàn số 0
- n * m <= 30
Min(n, m) <= 5
50
COWGIRL
Làm trâu
51
int calc(int i, int pre_mask) {
if (i > nCol)
return 1;
int res = 0;
for (int mask = 0; mask < (1 << nRow); ++mask)
if (i != 1 && invalid(mask, pre_mask))
continue;
else
res += calc(i + 1, mask);
return res;
}
//calc(1, 0)
52
Invalid
Làm sao xác định có hình vuông 2 * 2 nào không thỏa mãn
hay không?
53
int calc(int i, int pre_mask) {
if (i > nCol)
return 1;
int res = 0;
for (int mask = 0; mask < (1 << nRow); ++mask)
if (i != 1 && invalid(mask, pre_mask))
continue;
else
res += calc(i + 1, mask);
return res;
}
54
Độ phức tạp?
int calc(int i, int pre_mask) {
if (i > nCol)
return 1; Số lượng trạng thái:
int &res = f[i][pre_mask]; - i:
if (res != -1) return res; - Pre_mask:
res = 0;
for (int mask = 0; mask < (1 << nRow); ++mask)
if (i != 1 && invalid(mask, pre_mask)) Chi phí chuyển:
continue; - Duyệt mask:
else
- Kiểm tra, thao tác set:
res += calc(i + 1, mask);
return res;
}
Đoán xem làm gì tiếp theo
55
Invalid
56
Invalid
57
Invalid
//...
}
58
Invalid
59
Invalid
60
Q&A
61
Lưu ý
Bitmask KHÔNG làm thay đổi bản chất của hàm qhđ
62
Multi-test với đệ
quy có nhớ
63
Multi-test với đệ quy có nhớ
64
Multi-test với đệ quy có nhớ
65
Multi-test với đệ quy có nhớ
66