Hướng dẫn cho Chia hết cho 6 (THTA Vòng KV Nam 2025)


Chỉ sử dụng khi thực sự cần thiết như một cách tôn trọng tác giả và người viết hướng dẫn này.

Chép code từ bài hướng dẫn để nộp bài là hành vi có thể dẫn đến khóa tài khoản.

Tóm tắt đề bài

Cho một số tự nhiên \(N\) có tối đa \(10^5\) chữ số. Bạn được phép thay đổi tối đa 2 chữ số của \(N\) để tạo ra một số mới sao cho:

  1. Số mới chia hết cho 6.
  2. Số mới không có chữ số 0 ở đầu.
  3. Số mới là lớn nhất có thể.

Nếu không thể tạo được số nào thỏa mãn, in ra 0.

Phân tích

  • Điều kiện chia hết cho 6: Một số chia hết cho 6 khi và chỉ khi nó đồng thời chia hết cho 2 và 3.
    • Chia hết cho 2: Chữ số cuối cùng phải là số chẵn (\(0, 2, 4, 6, 8\)).
    • Chia hết cho 3: Tổng các chữ số của số đó phải chia hết cho 3.
  • Tính tham lam: Để số thu được là lớn nhất, ta nên ưu tiên thay đổi các chữ số ở vị trí bên trái (vị trí có trọng số lớn) thành các chữ số lớn nhất có thể (số 9).
  • Giới hạn thay đổi: Chúng ta chỉ có tối đa 2 lượt thay đổi. Điều này có nghĩa là ta cần tính toán cẩn thận để sau khi thay đổi một chữ số ở bên trái, ta vẫn còn đủ lượt thay đổi để sửa chữ số cuối (nếu cần) và sửa tổng các chữ số sao cho chia hết cho 3.

Hướng giải quyết

1. Hàm kiểm tra tính khả thi (feasible)

Với một trạng thái hiện tại (tổng các chữ số, chữ số cuối, số lượt thay đổi còn lại), ta cần kiểm tra xem có thể biến đổi số đó thành số chia hết cho 6 hay không:

  • Nếu còn 2 lượt thay đổi: Luôn có thể (dùng 1 lượt sửa chữ số cuối thành chẵn, 1 lượt sửa một chữ số bất kỳ để tổng chia hết cho 3).
  • Nếu còn 1 lượt thay đổi:
    • Nếu chữ số cuối đã chẵn: Chỉ cần kiểm tra xem có thể thay đổi một chữ số nào đó để tổng chia hết cho 3 không (luôn được trừ khi số chỉ có 1 chữ số và không tìm được số chẵn nào thỏa mãn).
    • Nếu chữ số cuối lẻ: Bắt buộc phải dùng lượt thay đổi duy nhất này để sửa chữ số cuối thành chẵn sao cho tổng mới chia hết cho 3.
  • Nếu còn 0 lượt thay đổi: Kiểm tra xem số hiện tại đã chia hết cho 6 chưa.

2. Chiến lược tham lam

  • Duyệt từ trái sang phải của số \(N\).
  • Tại mỗi vị trí \(i\), thử thay thế chữ số hiện tại bằng các chữ số từ \(9\) xuống \(cur+1\).
  • Nếu việc thay thế này vẫn đảm bảo ta có thể đưa số về dạng chia hết cho 6 trong số lượt thay đổi còn lại, ta thực hiện thay đổi ngay lập tức và giảm số lượt thay đổi đi 1.

3. Xử lý hậu kỳ

Sau khi đã cố gắng tăng các chữ số ở bên trái, nếu số vẫn chưa chia hết cho 6:

  • Nếu còn lượt thay đổi, ưu tiên sửa chữ số cuối cùng để vừa đảm bảo tính chẵn, vừa cố gắng đưa tổng về chia hết cho 3.
  • Nếu vẫn chưa được và còn lượt thay đổi, thực hiện sửa các chữ số ở vị trí ít quan trọng hơn (bên phải) để thỏa mãn điều kiện.

Độ phức tạp

  • Thời gian: \(O(n \times 10)\) với \(n\) là số chữ số của \(N\). Tại mỗi vị trí, ta chỉ thử thay đổi thành các chữ số từ 9 đến 0. Với \(n = 10^5\), thuật toán chạy tốt trong giới hạn thời gian.
  • Bộ nhớ: \(O(n)\) để lưu trữ các chữ số của \(N\).

Code tham khảo

Python
import sys

def feasible(sum_digits, last_digit, rem_changes, n):
    """
    Kiểm tra xem với số lượt thay đổi còn lại (rem_changes), 
    có thể đưa số về dạng chia hết cho 6 không.
    """
    mod3 = sum_digits % 3
    last_even = (last_digit % 2 == 0)

    if rem_changes >= 2:
        return True
    if rem_changes == 1:
        # Nếu đã thỏa mãn cả 2 điều kiện
        if last_even and mod3 == 0:
            return True
        # Nếu chữ số cuối chưa chẵn, bắt buộc phải đổi chữ số cuối
        if not last_even:
            for t in (8, 6, 4, 2, 0):
                if n > 1 and t == 0 and n == 1: # Trường hợp đặc biệt số có 1 chữ số
                    continue
                if (sum_digits - last_digit + t) % 3 == 0:
                    return True
            return False
        else:
            # Chữ số cuối đã chẵn, chỉ cần đổi 1 chữ số bất kỳ để mod 3 = 0
            return True 

    return last_even and (mod3 == 0)

def solve():
    s = sys.stdin.readline().strip()
    if not s:
        return

    n = len(s)
    digits = [int(ch) for ch in s]
    sum_digits = sum(digits)
    rem = 2

    # Bước 1: Tham lam từ trái sang phải để tăng giá trị số
    for i in range(n):
        if rem == 0:
            break
        cur = digits[i]
        # Thử thay đổi chữ số tại i thành d > cur để số lớn hơn
        for d in range(9, cur, -1):
            if i == 0 and d == 0: continue # Không để số 0 ở đầu

            new_sum = sum_digits - cur + d
            new_last = digits[-1] if i != n-1 else d

            if feasible(new_sum, new_last, rem - 1, n):
                digits[i] = d
                sum_digits = new_sum
                rem -= 1
                break

    # Bước 2: Nếu chưa chia hết cho 6, dùng lượt thay đổi còn lại để sửa
    if not (sum_digits % 3 == 0 and digits[-1] % 2 == 0):
        # Ưu tiên sửa chữ số cuối cùng (ít ảnh hưởng đến giá trị nhất)
        if rem >= 1:
            found = False
            for t in (8, 6, 4, 2, 0):
                if n > 1 and t == 0 and n == 1: continue
                new_sum = sum_digits - digits[-1] + t
                if new_sum % 3 == 0:
                    digits[-1] = t
                    sum_digits = new_sum
                    rem -= 1
                    found = True
                    break

            # Nếu vẫn chưa được và còn 2 lượt (trường hợp rem ban đầu = 2)
            if not found and rem >= 2:
                for t in (8, 6, 4, 2, 0):
                    target_sum = sum_digits - digits[-1] + t
                    need = (3 - (target_sum % 3)) % 3
                    for j in range(n-2, -1, -1):
                        for t2 in range(9, -1, -1):
                            if j == 0 and t2 == 0: continue
                            if (t2 - digits[j]) % 3 == need:
                                digits[-1] = t
                                digits[j] = t2
                                print(''.join(map(str, digits)))
                                return

    # Kiểm tra lại lần cuối trước khi in kết quả
    if sum_digits % 3 == 0 and digits[-1] % 2 == 0:
        print(''.join(map(str, digits)))
    else:
        print("0")

if __name__ == "__main__":
    solve()

Bình luận

Mới nhất
Tải bình luận...

Không có bình luận nào.