Bỏ qua để đến nội dung

Meet in the middle

Mức độ: Nâng cao

Đề cho n = 40 số, đếm số cách chọn một tập con có tổng bằng S. Thử hết thì có 240, khoảng 1012 tập con, quá chậm. Quy hoạch động theo tổng cũng không được nếu các số tới 109. Meet in the middle (gặp nhau ở giữa) chia dãy làm hai nửa, mỗi nửa 20 số chỉ có 220, khoảng 106 tập con. Liệt kê riêng từng nửa, rồi ghép hai nửa lại một cách thông minh.

Mỗi tập con của cả dãy gồm một phần nằm ở nửa trái và một phần nằm ở nửa phải. Tổng tập con bằng S khi:

(tổng phần bên trái) + (tổng phần bên phải) = S

Vậy với mỗi tổng x của nửa trái, chỉ cần đếm có bao nhiêu tổng của nửa phải bằng S - x. Đây chính là bài đếm cặp có tổng bằng S giữa hai danh sách, làm bằng dict/map đếm tần suất hoặc sắp xếp rồi tìm kiếm nhị phân.

Dãy 3 5 6 7, S = 11, chia thành 3 5 | 6 7:

Tập con nửa tráiTổng xCần S - xSố tập con nửa phải có tổng đó
(rỗng)0110
3380
5561 (tập 6)
3, 5830

Tổng các tập con nửa phải: (rỗng) 0, 6, 7, 13. Đáp số 1: tập 5, 6.

Số bước: liệt kê 2 × 2n/2 tổng, rồi mỗi tổng nửa trái tra một lần. Với n = 40 là khoảng 2 × 106 bước thay vì 1012, ít hơn khoảng 500 000 lần.

Liệt kê tổng mọi tập con của một nửa bằng cách nhân đôi dần: bắt đầu với danh sách [0] (tập rỗng). Với mỗi số x, mọi tập con cũ hoặc không lấy x, hoặc lấy thêm x, nên danh sách mới gồm các tổng cũ và các tổng cũ cộng x.

Đã xétCác tổng
(chưa có số nào)0
30, 3
3, 50, 3, 5, 8
import sys
from collections import Counter
def cac_tong(day):
tong = [0]
for x in day:
tong += [t + x for t in tong]
return tong
du_lieu = sys.stdin.read().split()
n, s = int(du_lieu[0]), int(du_lieu[1])
a = list(map(int, du_lieu[2:2 + n]))
trai = cac_tong(a[:n // 2])
dem_phai = Counter(cac_tong(a[n // 2:]))
print(sum(dem_phai[s - x] for x in trai))

Lưu ý: cách đếm này tính cả tập rỗng. Nếu đề chỉ đếm tập con khác rỗng và S = 0 thì trừ đi 1.

Ghép hai nửa dùng được cho nhiều kiểu câu hỏi, chỉ cần sắp xếp danh sách nửa phải:

Câu hỏiVới mỗi tổng x của nửa trái
đếm tập con có tổng bằng Sđếm các tổng nửa phải bằng S - x
tổng lớn nhất không quá Stìm tổng nửa phải lớn nhất không quá S - x (tìm kiếm nhị phân)
đếm tập con có tổng không quá Sđếm các tổng nửa phải không quá S - x
tổng gần S nhấtxét hai tổng nửa phải đứng hai bên S - x

Ý tưởng chung: thay vì thử mọi cặp (trái, phải), với mỗi phần trái ta biết chính xác phần phải cần là gì, rồi tra nhanh trong danh sách đã chuẩn bị.

Gồm 20 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ố.

Dòng đầu có n, S (1 ≤ n ≤ 40, |S| ≤ 1015), dòng thứ hai có n số nguyên (|ai| ≤ 1012). In ra số tập con (kể cả tập rỗng) có tổng bằng S.

Ví dụ:

NhậpIn ra
4 11
3 5 6 7
1

Kiểm tra: nhập

20 0
1 -2 3 -4 5 -6 7 -1 2 -3 4 -5 6 -7 1 -2 3 -4 5 -6
Xem đáp số

42040

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, S (1 ≤ n ≤ 40, 0 ≤ S ≤ 1015), dòng thứ hai có n số nguyên dương (ai ≤ 1012). In ra YES nếu có một tập con khác rỗng có tổng bằng S, ngược lại in ra NO.

Ví dụ:

NhậpIn ra
5 23
10 4 8 3 15
YES

Kiểm tra: nhập

20 4000002478
1000000000 1000000007 1000000028 1000000063 1000000112 1000000175 1000000252 1000000343 1000000448 1000000567 1000000700 1000000847 1000001008 1000001183 1000001372 1000001575 1000001792 1000002023 1000002268 1000002527
Xem đáp số

YES

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, S (1 ≤ n ≤ 40, 0 ≤ S ≤ 1015), dòng thứ hai có n số nguyên dương (ai ≤ 1012). In ra tổng lớn nhất của một tập con (có thể rỗng) không vượt quá S.

Ví dụ:

NhậpIn ra
5 20
7 11 4 13 8
20

Kiểm tra: nhập

20 23456789012
33333310 79012289 154320876 266666473 423456482 632098305 899999344 1234567001 1643208678 2133331777 2712343700 3387651849 4166663626 5056786433 6065427672 7199994745 8467895054 9876536001 1433324988 3145669417
Xem đáp số

23456773012

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, S (1 ≤ n ≤ 40, |S| ≤ 1015), dòng thứ hai có n số nguyên (|ai| ≤ 1012). In ra giá trị nhỏ nhất của |(tổng tập con) - S| trên mọi tập con (kể cả tập rỗng).

Ví dụ:

NhậpIn ra
4 -7
5 -3 9 -2
2

Kiểm tra: nhập

20 77777777777
987654321 -1975308642 2962962963 -3950617284 4938271605 -5925925926 6913580247 -7901234568 8888888889 -9876543210 10864197531 -11851851852 12839506173 -13827160494 14814814815 -15802469136 16790123457 -17777777778 18765432099 -19753086420
Xem đáp số

246913582

Hướng dẫn giải chi tiết: liên hệ tác giả.

Có n bạn, bạn thứ i nặng ai kg. Chia n bạn thành hai đội (đội có thể không có ai) sao cho chênh lệch tổng cân nặng hai đội nhỏ nhất. Dòng đầu có n (1 ≤ n ≤ 40), dòng thứ hai có n số ai (1 ≤ ai ≤ 1012). In ra chênh lệch nhỏ nhất.

Ví dụ:

NhậpIn ra
5
40 25 32 18 55
4

Kiểm tra: nhập

20
100000000000 100000031337 100000250696 100000846099 100002005568 100003917125 100006768792 100010748591 100016044544 100022844673 100031337000 100041709547 100054150336 100068847389 100085988728 100105762375 100128356352 100153958681 100182757384 100214940483
Xem đáp số

0

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, L, R (1 ≤ n ≤ 40, |L|, |R| ≤ 1015, L ≤ R), dòng thứ hai có n số nguyên (|ai| ≤ 1012). In ra số tập con (kể cả tập rỗng) có tổng từ L đến R.

Ví dụ:

NhậpIn ra
4 5 10
2 3 5 8
7

Kiểm tra: nhập

20 -100 100
1 -38 75 -11 48 -85 21 -58 95 -31 68 -4 41 -78 14 -51 88 -24 61 -98
Xem đáp số

575214

Hướng dẫn giải chi tiết: liên hệ tác giả.

Có n đồng xu, đồng thứ i có mệnh giá ai (mỗi đồng dùng nhiều nhất một lần). Dòng đầu có n, S (1 ≤ n ≤ 40, 1 ≤ S ≤ 1015), dòng thứ hai có n số ai (1 ≤ ai ≤ 1012). In ra số đồng xu ít nhất để trả đúng S, hoặc -1 nếu không trả được.

Ví dụ:

NhậpIn ra
6 15
1 5 7 8 3 6
2

Kiểm tra: nhập

20 6004654065
1000000000 1000012345 1000049380 1000111105 1000197520 1000308625 1000444420 1000604905 1000790080 1000999945 1001234500 1001493745 1001777680 1002086305 1002419620 1002777625 1003160320 1003567705 1003999780 1004456545
Xem đáp số

6

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, S (1 ≤ n ≤ 40, 1 ≤ S ≤ 1015), dòng thứ hai có n số nguyên dương (ai ≤ 1012). In ra tổng nhỏ nhất của một tập con mà tổng đó ít nhất S, hoặc -1 nếu tổng cả dãy còn nhỏ hơn S.

Ví dụ:

NhậpIn ra
5 20
6 9 14 3 11
20

Kiểm tra: nhập

20 1232739991
32891189 39143233 45938933 53278289 61161301 69587969 78558293 88072273 98129909 108731201 119876149 131564753 143797013 156572929 169892501 183755729 198162613 213113153 228607349 244645201
Xem đáp số

1232739991

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, k (1 ≤ n ≤ 40, |k| ≤ 109), dòng thứ hai có n số nguyên (|ai| ≤ 109). In ra số tập con khác rỗng có trung bình cộng đúng bằng k.

Ví dụ:

NhậpIn ra
5 4
2 6 4 9 3
3

Kiểm tra: nhập

20 7
3 8 4 9 5 10 6 11 7 3 8 4 9 5 10 6 11 7 3 8
Xem đáp số

67823

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, m (1 ≤ n ≤ 40, 1 ≤ m ≤ 109), dòng thứ hai có n số nguyên (0 ≤ ai ≤ 109). In ra số tập con khác rỗng có tổng chia hết cho m.

Ví dụ:

NhậpIn ra
4 5
1 4 6 9
5

Kiểm tra: nhập

20 1009
123457 246914 370371 493828 617285 740742 864199 987656 111110 234567 358024 481481 604938 728395 851852 975309 98763 222220 345677 469134
Xem đáp số

1555

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, m (1 ≤ n ≤ 40, 1 ≤ m ≤ 109), dòng thứ hai có n số nguyên (0 ≤ ai ≤ 109). In ra giá trị lớn nhất của (tổng tập con) chia lấy dư cho m, xét mọi tập con kể cả tập rỗng.

Ví dụ:

NhậpIn ra
4 10
5 2 4 1
9

Kiểm tra: nhập

20 999999937
31415926 62831852 94247778 125663704 157079630 188495556 219911482 251327408 282743334 314159260 345575186 376991112 408407038 439822964 471238890 502654816 534070742 565486668 596902594 628318520
Xem đáp số

995132486

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu có n, x (1 ≤ n ≤ 40, 0 ≤ x nhỏ hơn 260), dòng thứ hai có n số nguyên (0 ≤ ai nhỏ hơn 260). In ra số tập con (kể cả tập rỗng) có XOR các phần tử bằng x (XOR của tập rỗng bằng 0).

Ví dụ:

NhậpIn ra
4 6
3 5 6 1
2

Kiểm tra: nhập

20 1023
0 433 866 275 708 117 550 983 392 825 234 667 76 509 942 351 784 193 626 35
Xem đáp số

1024

Hướng dẫn giải chi tiết: liên hệ tác giả.

Có n đồ vật, vật thứ i nặng wi và có giá trị vi. Chọn một số vật bỏ vào vali chịu được nhiều nhất W. Dòng đầu có n, W (1 ≤ n ≤ 40, 0 ≤ W ≤ 1015), n dòng sau mỗi dòng có w, v (1 ≤ w, v ≤ 1012). In ra tổng giá trị lớn nhất.

Ví dụ:

NhậpIn ra
4 10
5 10
4 40
6 30
3 50
90

Kiểm tra: nhập

18 300000000000
14000000000 1
13000000000 104730
12000000000 209459
11000000000 314188
10000000000 418917
9000000000 523646
8000000000 628375
7000000000 733104
6000000000 837833
5000000000 942562
4000000000 1047291
3000000000 1152020
2000000000 1256749
1000000000 1361478
90000000000 1466207
89000000000 1570936
88000000000 1675665
87000000000 1780394
Xem đáp số

12986412

Hướng dẫn giải chi tiết: liên hệ tác giả.

Một cái cân hai đĩa, đồ vật nặng S đặt ở đĩa trái. Có n quả cân, quả thứ i nặng ai, mỗi quả đặt lên đĩa trái, đĩa phải hoặc không dùng. Dòng đầu có n, S (1 ≤ n ≤ 24, 1 ≤ S ≤ 1012), dòng thứ hai có n số ai (1 ≤ ai ≤ 1011). In ra số cách đặt quả cân để hai đĩa cân bằng.

Ví dụ:

NhậpIn ra
3 5
1 3 9
1

Kiểm tra: nhập

12 1000
1 4 11 30 85 248 735 8 11 18 37 92
Xem đáp số

182

Hướng dẫn giải chi tiết: liên hệ tác giả.

Có n loại tiền, loại thứ i mệnh giá ai, mỗi loại có đúng hai tờ. Dòng đầu có n, S (1 ≤ n ≤ 24, 0 ≤ S ≤ 1015), dòng thứ hai có n số ai (1 ≤ ai ≤ 1012, có thể trùng). In ra số cách chọn số tờ dùng của mỗi loại (0, 1 hoặc 2 tờ) để tổng đúng S.

Ví dụ:

NhậpIn ra
3 7
1 2 5
2

Kiểm tra: nhập

12 60
1 2 3 5 6 7 9 10 11 4 5 6
Xem đáp số

10095

Hướng dẫn giải chi tiết: liên hệ tác giả.

Nhóm 4. Ghép hai nửa của một biểu thức

Phần tiêu đề “Nhóm 4. Ghép hai nửa của một biểu thức”

Có bốn dãy A, B, C, D, mỗi dãy n số nguyên. Dòng đầu có n (1 ≤ n ≤ 1000), bốn dòng sau lần lượt là bốn dãy (|giá trị| ≤ 109). In ra số bộ (a, b, c, d), lấy a từ A, b từ B, c từ C, d từ D (theo vị trí), có a + b + c + d = 0.

Ví dụ:

NhậpIn ra
2
1 2
-2 -1
-1 2
0 2
2

Kiểm tra: nhập

8
3 -1 4 1 -5 9 -2 6
5 3 -5 8 9 7 -9 3
2 -3 8 4 -6 2 6 -4
-3 3 8 -3 2 7 9 5
Xem đáp số

103

Hướng dẫn giải chi tiết: liên hệ tác giả.

Bài 17. Bộ bốn chỉ số có tổng bằng S

Phần tiêu đề “Bài 17. Bộ bốn chỉ số có tổng bằng S”

Dòng đầu có n, S (4 ≤ n ≤ 1000, |S| ≤ 4 × 109), dòng thứ hai có n số nguyên (|ai| ≤ 109). In ra số bộ chỉ số i, j, k, l với i đứng trước j, j trước k, k trước l và ai + aj + ak + al = S.

Ví dụ:

NhậpIn ra
6 10
1 2 3 4 5 1
3

Kiểm tra: nhập

10 0
-3 1 2 -1 0 3 -2 1 -1 2
Xem đáp số

26

Hướng dẫn giải chi tiết: liên hệ tác giả.

Giỏ thứ nhất có n quả, giỏ thứ hai có m quả, mỗi quả có khối lượng. Chọn một nhóm quả (có thể rỗng) ở giỏ thứ nhất và một nhóm ở giỏ thứ hai có tổng khối lượng bằng nhau. Dòng đầu có n, m (1 ≤ n, m ≤ 20), hai dòng sau là khối lượng các quả của hai giỏ (1 ≤ khối lượng ≤ 109). In ra số cách chọn (cả hai nhóm đều rỗng cũng là một cách).

Ví dụ:

NhậpIn ra
3 3
1 2 3
3 1 5
7

Kiểm tra: nhập

10 10
1 2 3 4 5 1 2 3 4 5
1 2 3 4 5 6 7 1 2 3
Xem đáp số

48848

Hướng dẫn giải chi tiết: liên hệ tác giả.

Bảng n hàng, m cột, mỗi ô ghi một số. Đi từ ô trên cùng bên trái đến ô dưới cùng bên phải, mỗi bước sang phải hoặc xuống dưới. Dòng đầu có n, m, k (1 ≤ n, m ≤ 20, 0 ≤ k nhỏ hơn 260), n dòng sau mỗi dòng có m số (0 ≤ số nhỏ hơn 260). In ra số đường đi có XOR các ô trên đường (kể cả ô đầu và ô cuối) bằng k.

Ví dụ:

NhậpIn ra
3 3 11
2 1 5
7 10 0
12 6 4
3

Kiểm tra: nhập

8 8 5
0 4 8 12 3 7 11 2
5 9 0 4 8 12 3 7
10 1 5 9 0 4 8 12
2 6 10 1 5 9 0 4
7 11 2 6 10 1 5 9
12 3 7 11 2 6 10 1
4 8 12 3 7 11 2 6
9 0 4 8 12 3 7 11
Xem đáp số

48

Hướng dẫn giải chi tiết: liên hệ tác giả.

Cho tập T gồm n số nguyên khác nhau. Dòng đầu có n (1 ≤ n ≤ 100), dòng thứ hai có n số (|giá trị| ≤ 30000). In ra số bộ sáu (a, b, c, d, e, f), mỗi số lấy từ T (được lặp lại), thỏa mãn d khác 0 và (a × b + c) / d - e = f.

Ví dụ:

NhậpIn ra
2
2 3
4

Kiểm tra: nhập

10
-7 -3 -1 0 1 2 4 5 9 12
Xem đáp số

10529

Hướng dẫn giải chi tiết: liên hệ tác giả.