Tuyển sinh 🎓 Mình nhận đào tạo lập trình giải thuật thi đấu: thi vào lớp 10 chuyên tin · thi học sinh giỏi tin học cấp tỉnh/thành · thi vào đội tuyển quốc gia · Olympic tin học sinh viên / ICPC · phỏng vấn thuật toán. Bấm vào đây để xem thông tin khóa học → Xem khóa học

Tổng ô đi qua (MOVSUM)

Tổng ô đi qua (MOVSUM)
Nguồn: CLUE OJ – Chương 22: Quy hoạch động (MOVSUM) Độ khó: Khá Quy hoạch động Max tiền tố

Đề bài

Bờm có một bảng kích thước $1 \times N$ chia thành $N$ ô vuông được đánh số $1, 2, \ldots, N$ từ trái sang phải. Trên ô có chỉ số $i$ ghi số nguyên $a_i$.

Bờm chơi trò chơi di chuyển hai quân cờ trên bảng này. Quân cờ bên trái có màu xanh, ban đầu đặt ở ô 1, quân cờ bên phải có màu đỏ, ban đầu đặt ở ô $N$. Bờm có thể di chuyển bất kì quân cờ nào, số lần tuỳ ý, miễn là tuân thủ các quy tắc sau:

  • Quân cờ xanh chỉ có thể di chuyển sang phải (tức là đến ô có chỉ số lớn hơn chỉ số ô hiện tại).
  • Quân cờ đỏ chỉ có thể di chuyển sang trái (tức là đến ô có chỉ số nhỏ hơn chỉ số ô hiện tại).
  • Mỗi bước di chuyển chỉ có thể dịch quân cờ qua số ô là một trong $K$ số $s_1, s_2, \ldots, s_K$.
  • Hai quân cờ không thể đứng trên cùng một ô, cũng không thể vượt qua nhau (tức là quân cờ xanh không thể di chuyển đến ô chỉ số lớn hơn hoặc bằng chỉ số của ô chứa quân cờ đỏ, và ngược lại).

Điểm Bờm nhận được trong trò chơi bằng tổng các số trong các ô mà các quân cờ đã đứng (kể cả hai ô xuất phát). Hãy giúp Bờm tính toán xem tổng điểm lớn nhất có thể đạt được là bao nhiêu.

Dữ liệu:

  • Dòng 1: hai số nguyên $N, K$ $(1 < N \le 10^5;\ 1 < K \le 10^3)$.
  • Dòng 2: $N$ số nguyên $a_1, a_2, \ldots, a_N$ $(-10^3 \le a_i \le 10^3)$.
  • Dòng 3: $K$ số nguyên $s_1, s_2, \ldots, s_K$ $(1 \le s_i \le N - 1)$.

Kết quả: một số nguyên là tổng điểm lớn nhất có thể đạt được.

Subtask: 20% điểm đầu tiên với $N, K \le 10$; 30% điểm tiếp theo với $N \le 10^4$; 50% điểm còn lại với $N \le 10^5$.

Ví dụ – input
9 2
3 2 6 6 0 2 4 5 5
3 2
Ví dụ – output
24

Minh hoạ ví dụ: ô xanh là các ô quân xanh đã đứng, ô đỏ là các ô quân đỏ đã đứng.

326602455

Quân xanh đi $1 \to 3$ (bước 2), quân đỏ đi $9 \to 7 \to 4$ (bước 2 rồi bước 3). Tổng điểm $= (3 + 6) + (5 + 4 + 6) = 24$.

Lời mở đầu

Thoạt nhìn bài có vẻ rắc rối vì 2 quân cờ di chuyển xen kẽ, không được vượt nhau. Nhưng chỉ cần một nhận xét nhỏ là bài tách thành hai bài quy hoạch động độc lập rất quen thuộc, rồi ghép lại bằng kỹ thuật max tiền tố. Mình đi lần lượt: vét cạn (subtask 1) → quy hoạch động + thử mọi cặp (subtask 2) → thêm max tiền tố (full điểm), mỗi cách đều nộp thử trên bộ test thật.

Dành cho học viên · Ưu tiênNộp bài trên hệ thống CLUE OJ

Link này chỉ học viên đã tham gia tổ chức mới vào được. Học viên nên ưu tiên nộp ở đây vì sẽ được chấm nhanh hơn.

Nộp bài trên CLUE OJ

Lộ trình lời giải, bấm vào từng phần để nhảy tới:

Bộ testBộ test chính thức có gì?

Test$N$$K$$a_i$$s_i$ lớn nhấtĐáp án
001–0203 – 102 – 10-20 → 209-25 → 47
021–05010.000 – 10.0001.000 – 1.000-999 → 9999.999158.390 → 928.578
051–100100.000 – 100.0001.000 – 1.000-999 → 99932.768251.565 → 4.304.919

Bộ test khớp đúng 3 subtask của đề: 20 test đầu $N, K \le 10$; 30 test tiếp theo $N = 10^4$; 50 test cuối $N = 10^5$ (tất cả test lớn đều có $K = 1000$). Một vài điểm cần lưu ý:

  • Các bước $s_i$ có thể trùng nhau (95 test). Không sao cả, chỉ là thử lại một bước đã thử.
  • Đáp án có thể âm (6 test nhỏ): hai quân cờ đứng yên vẫn bị tính 2 ô xuất phát, mà 2 ô đó có thể âm. Vậy đừng khởi tạo đáp án bằng 0.
  • Có những ô không tới được (ví dụ mọi bước đều chẵn thì quân xanh chỉ tới các ô lẻ). Phải đánh dấu riêng các ô này, không được coi tổng của chúng là 0.
  • Đáp án lớn nhất cỡ $4{,}3 \cdot 10^6$ nên int cũng đủ, nhưng mình vẫn dùng long long cho an toàn.
SubtaskĐiều kiệnTestCách 1
vét cạn
Cách 2
QHĐ + thử mọi cặp
Cách 3
QHĐ + max tiền tố
1 (20%)$N, K \le 10$001–020quaquaqua
2 (30%)$N \le 10^4$021–050quá thời gianquaqua
3 (50%)$N \le 10^5$051–100quá thời gianquá thời gianqua

Ý tưởngTách thành 2 quân đi riêng, ghép ở chỗ dừng

Nhận xét quan trọng: gọi $i$ là ô cuối cùng quân xanh dừng, $j$ là ô cuối cùng quân đỏ dừng. Mọi ô quân xanh từng đứng đều $\le i$, mọi ô quân đỏ từng đứng đều $\ge j$. Nếu $i < j$ thì ta cứ đi hết quân xanh trước rồi mới đi quân đỏ – không bao giờ có chuyện đụng hay vượt nhau. Vậy luật "không vượt nhau" chỉ còn là điều kiện $i < j$, còn 2 quân đi hoàn toàn độc lập.

Hai bảng quy hoạch động
$f[i]$ = tổng lớn nhất khi quân xanh đi từ ô 1 và dừng ở ô $i$: $f[1] = a_1$, $f[i] = a_i + \max_t f[i - s_t]$.
$g[j]$ = tổng lớn nhất khi quân đỏ đi từ ô $N$ và dừng ở ô $j$: $g[N] = a_N$, $g[j] = a_j + \max_t g[j + s_t]$.
(Ô không tới được thì $f$ hay $g$ bằng $-\infty$.) Đáp án $= \max_{i < j} \big(f[i] + g[j]\big)$.

Tính $f$ và $g$ mất $O(NK)$. Phần ghép: thay vì thử mọi cặp $(i, j)$ – $O(N^2)$ – ta duyệt $j$ từ trái sang phải và giữ best = max(f[1..j−1]), mỗi bước chỉ cập nhật thêm $f[j-1]$. Khi đó đáp án $= \max_j (\text{best} + g[j])$, chỉ $O(N)$.

Em nhập dữ liệu rồi bấm Tiếp để xem 3 giai đoạn: tính $f$ (xanh), tính $g$ (đỏ), rồi quét max tiền tố:

Ví dụ nhanh:

Cách 1Vét cạn mọi đường đi của từng quân

Dùng nhận xét trên, ta liệt kê mọi đường đi của quân xanh (ô dừng, tổng điểm) bằng đệ quy, tương tự với quân đỏ, rồi thử mọi cặp có ô dừng xanh < ô dừng đỏ.

C++▶ Chạy tay từng dòng codeCách 1: liệt kê mọi đường đi của từng quân bằng đệ quy rồi ghép mọi cặp có ô dừng xanh < ô dừng đỏ.
Dữ liệu mẫu:
Bên phải là ngăn xếp lời gọi; hai danh sách xanh, doo lớn dần theo số đường đi.
Không giantheo số đường đi
Thời gianmũ – cỡ $O(K^N)$

Kết quả chạy thật: đúng 20/100 test – quá thời gian test 021–100. Số đường đi tăng theo cấp số nhân, $N = 10^4$ là "chết" ngay.

Cách 2Quy hoạch động f, g rồi thử mọi cặp

Thay vì liệt kê từng đường, mỗi ô chỉ cần nhớ tổng lớn nhất để dừng ở đó: chính là $f[i]$ và $g[j]$. Sau đó thử mọi cặp $i < j$.

C++▶ Chạy tay từng dòng codeCách 2: tính f (quân xanh) và g (quân đỏ) bằng QHĐ, rồi thử mọi cặp ô dừng i < j.
Dữ liệu mẫu:
  1. NEG = -1e18 đánh dấu ô không tới được. Phải kiểm tra != NEG trước khi cộng, nếu không $-10^{18} + 999$ sẽ thành một số "hợp lệ" nhầm.
  2. f tính từ trái sang phải (ô $i - s_t$ đã tính trước), g tính từ phải sang trái (ô $j + s_t$ đã tính trước).
Không gian$O(N + K)$
Thời gian$O(NK + N^2)$

Kết quả chạy thật: đúng 50/100 test – quá thời gian test 051–100. Với $N = 10^5$ phần thử cặp tốn $5 \cdot 10^9$ phép tính nên quá thời gian.

Cách 3Thêm max tiền tố – full điểm

Giữ nguyên phần tính $f$, $g$; chỉ thay 2 vòng lặp thử cặp bằng 1 vòng quét $j$ với biến best.

C++▶ Chạy tay từng dòng codeCách 3: như cách 2 nhưng ghép bằng best = max(f[1..j−1]) – O(N·K).
Dữ liệu mẫu:
  1. best được cập nhật bằng f[j - 1] trước khi dùng, nên luôn bằng $\max(f[1..j-1])$ – đúng điều kiện $i < j$.
  2. Tổng thời gian $O(NK + N)$: với $N = 10^5$, $K = 10^3$ là $2 \cdot 10^8$ phép tính đơn giản, C++ chạy chưa tới nửa giây (đo thật chậm nhất 0,45 giây khi máy đang bận).
Không gian$O(N + K)$
Thời gian$O(NK)$

Kết quả chạy thật: đúng 100/100 test, chậm nhất 0,448 giây.

Bản Python (mình viết thêm, chấm trên cùng bộ test): cách 1 được 20/25 test – quá thời gian test 021–025 (mình chỉ chạy thử test 001–025 vì các test lớn hơn chắc chắn quá thời gian); cách 2 được 20/28 test – quá thời gian test 021–025, 051–053 (mình chỉ chạy thử test 001–025, 051–053 vì các test lớn hơn chắc chắn quá thời gian); cách 3 được 20/28 test – quá thời gian test 021–025, 051–053 (mình chỉ chạy thử test 001–025, 051–053 vì các test lớn hơn chắc chắn quá thời gian). Phần tính $f$, $g$ tốn $N \cdot K$ phép tính: $10^7$ ở subtask 2 và $10^8$ ở subtask 3 – quá sức Python trong 1 giây, nên bản Python chỉ ăn được subtask 1; muốn full điểm thì nộp C++.

Ghi chú của mình: C++ – cách 1 (vét cạn) được 20/100 test; cách 2 (QHĐ + thử mọi cặp) được 50/100 test; cách 3 (QHĐ + max tiền tố) đạt 100/100 test. Mấu chốt của bài là nhận ra 2 quân cờ đi độc lập, chỉ cần ô dừng xanh < ô dừng đỏ.
Rất vui khi anh em nào đã đọc đến những dòng cuối cùng này. Anh em đọc chắc cũng mỏi mắt luôn chứ 😅 vậy anh em nghĩ người viết ra hết đống ở trên còn thế nào nữa 🤪. Vậy nên đừng tiếc gì cho mình một lời bình luận hoặc chia sẻ bài viết để động viên mình nè. Chúc anh em học tốt và gặt hái nhiều thành công 😇

Nhận xét