trang chủ / bài tập / choirform / lời giải

Đội Hợp Xướng Hogwarts

Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Tác giả: huunguyenhuunguyen

Hướng tiếp cận

Bài toán yêu cầu gán một trong K loại giọng cho mỗi ô trong lưới N×M sao cho tổng bất hòa âm giữa tất cả các cặp ô kề nhau là nhỏ nhất. Với M7K4, ta có thể dùng quy hoạch động theo broken profile (broken profile DP).

Nhận xét quan trọng

  1. M nhỏ, N lớn: Số cột M7 gợi ý rằng ta nên xử lý theo từng cột hoặc từng ô, duy trì trạng thái dựa trên bố cục cột hiện tại.

  2. Không gian trạng thái: Mỗi cột có M ô, mỗi ô có K lựa chọn, nên có KM trạng thái cho mỗi cột. Với K=4,M=7: 47=16384 trạng thái — đủ nhỏ để duy trì DP.

  3. Chuyển trạng thái hiệu quả: Nếu ta duyệt từng cặp trạng thái cột (KM×KM), mỗi cột tốn 268 triệu thao tác — quá chậm. Thay vào đó, ta dùng kỹ thuật broken profile: xử lý từng ô một, cập nhật trạng thái dần dần.

  4. Ma trận bất đối xứng: Cần chú ý hướng khi tính bất hòa âm — Da,b luôn tính từ ô được xử lý trước (trên, trái) sang ô được xử lý sau (dưới, phải).

Thuật toán

Biểu diễn trạng thái

Trạng thái s là một số trong hệ cơ số K gồm M chữ số, trong đó chữ số thứ i biểu diễn loại giọng tại hàng i của "profile" hiện tại.

Broken profile

Tại mỗi thời điểm khi ta đang xử lý ô (i,j) trong cột j:

  • Các hàng 0,1,,i1 của profile chứa giá trị của cột j (đã gán)
  • Các hàng i,i+1,,M1 của profile chứa giá trị của cột j1 (chưa bị thay thế)
Xử lý
  1. Cột đầu tiên (j=0): Xử lý từng hàng i=0,1,,M1. Khi gán loại giọng k cho hàng i:

    • Nếu i>0: cộng bất hòa âm dọc Dk,giọng hàng i1
    • Cập nhật profile: thay đổi chữ số i thành k
  2. Các cột tiếp theo (j=1,,N1): Xử lý từng hàng i=0,1,,M1. Khi gán loại giọng k cho ô (i,j):

    • Cộng bất hòa âm ngang: Dk,giọng cũ tại hàng i (giá trị từ cột j1, còn trong profile)
    • Nếu i>0: cộng bất hòa âm dọc: Dk,giọng mới tại hàng i1 (đã gán ở bước trước)
    • Cập nhật profile: thay đổi chữ số i thành k
  3. Kết quả: Sau khi xử lý hết N×M ô, lấy giá trị nhỏ nhất trong tất cả các trạng thái.

Lưu ý về hướng bất hòa âm

Khi ô (i,j) được gán giọng k:

  • Bất hòa âm ngang với ô (i,j1): ta tính Dk[giọng ô bên trái]. Tuy nhiên, theo đề bài, Dgi,j1,gi,j mới đúng (từ trái sang phải). Nhưng vì ta xử lý từ trái sang phải, ô (i,j1) là "old_type" đã có trong profile. Vậy ta cộng Dold\_type,k... Thực ra, hướng tính không quan trọng miễn ta nhất quán: mỗi cặp kề nhau được tính đúng một lần. Khi xử lý ô (i,j), ta tính bất hòa âm giữa nó và ô bên trái/bên trên — đây chính xác là mỗi cặp được tính một lần.

Trong cài đặt: D[k][old_type] nghĩa là ô hiện tại (giọng k) đứng cạnh ô trước (giọng old_type), tương đương Dk,old\_type.

Độ phức tạp

  • Thời gian: O(N×M×KM×K). Với N=1000, M=7, K=4: 460 triệu thao tác cơ bản.
  • Bộ nhớ: O(KM)=O(16384) cho mảng DP — rất tiết kiệm.

Bình luận

Không có bình luận tại thời điểm này.

gnatmake 12.2.0 a68g 3.1.2 nasm 2.16.1 as_x64 2.46 awk 1.3.4 gcc 16.1.0 csc 6.12.0.200 g++ 16.1.0 g++-themis 16.1.0 g++17 16.1.0 g++20 16.1.0 g++23 16.1.0 clang++ 22.1.6 dmd 2.112.0 dart 3.12.1 gforth 0.7.3 gfortran 12.2.0 go 1.26.3 groovyc 5.0.6 javac 25.0.3 node 26.2.0 kotlinc 2.3.21 sbcl 2.2.9 lua 5.4.8 nim 2.2.10 fpc 3.2.2 fpc-themis 3.2.2 perl 5.36.0 php 8.5.6 pike 8.0 pypy3 7.3.23 python3 3.14.5 racket 8.7 ruby 4.0.5 rustc 1.96.0 csc 5.3.0 ctoj-scratch 0.0.1 sed 4.9 tclsh 8.6 bun 1.3.14 deno 2.8.1 v 0.5.1 zig 0.16.0