Hướng dẫn giải của Trò chơi quân sự


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.

Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.

Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.

Bạn phải đăng nhập để mở lời giải này.

Đăng nhập

Tác giả: Hiếu Nguyễn, hohoanghai5042011, lephuochauhungvuong, nguyentienloi

Hiểu bài toán

Bài toán yêu cầu tìm số lần bắn đạn pháo tối thiểu để phá hủy toàn bộ một bản đồ quân sự dạng lưới n×m. Khi bắn vào một ô, viên đạn sẽ phá hủy chính ô đó và tất cả các ô kề cạnh (4 ô xung quanh). Tuy nhiên, theo mô tả và các hình ảnh minh họa, cơ chế phá hủy thực tế là viên đạn trúng vào một ô sẽ phá hủy hình chữ nhật 3×3 (9 ô) bao quanh tâm điểm trúng đạn. Nếu viên đạn nằm ở biên, nó sẽ phá hủy một hình chữ nhật nhỏ hơn. Mục tiêu là tìm số lượng viên đạn ít nhất sao cho mọi ô trong lưới n×m đều bị phá hủy ít nhất một lần. Lưới có thể rất lớn (n, m lên tới 10^6), yêu cầu thuật toán hiệu quả ~O(1)~.

Các cách tiếp cận

Cách Tối ưu hóa vị trí bắn
#include <iostream>
using namespace std;

int main() {
    long long n, m;
    cin >> n >> m;
    // Bắn vào các ô có tọa độ (2, 2), (2, 5), ... (5, 2), ...
    // Mỗi viên đạn bao phủ 3 hàng và 3 cột.
    // Số lượng cần thiết là số lượng vị trí dọc * số lượng vị trí ngang.
    long long ans = ((n + 1) / 2) * ((m + 1) / 2);
    cout << ans;
    return 0;
}
  • Time Complexity: O(1)
  • Space Complexity: O(1)

Đây là cách tiếp cận tối ưu dựa trên việc quan sát quy luật bao phủ. Mỗi viên đạn bắn vào ô (i, j) phá hủy các ô từ hàng i-1 đến i+1 và cột j-1 đến j+1. Để tối ưu, ta nên đặt các viên đạn sao cho chúng bao phủ lưới mà không trùng lặp quá mức cần thiết. Mô hình tối ưu là đặt các viên đạn cách nhau 3 đơn vị (ví dụ tại các ô (2,2), (2,5), ...). Điều này tạo ra các khối 3x3 (hoặc 2x2, 2x3, ... ở biên) bao phủ hoàn toàn lưới. Công thức tính số vị trí là ((n + 1) / 2) * ((m + 1) / 2). Ví dụ n=2, m=2: ((2+1)/2)=1, ((2+1)/2)=2? Không, 1*1=1. Ví dụ n=3, m=3: ((3+1)/2)=2, ((3+1)/2)=2 => 4. Điều này đúng với logic bao phủ.

Cách Mô phỏng chia lưới (Bing Chilling)
#include <iostream>
using namespace std;

int main() {
    long long n, m;
    cin >> n >> m;
    // Chia lưới thành các khối 3x3.
    // Nếu chia hết cho 3, số viên đạn là (n/3) * (m/3).
    // Nếu dư, ta cần bắn thêm để che phần dư.
    // Tuy nhiên, cách này phức tạp hơn công thức tối ưu.
    // Công thức tối ưu là ((n + 1) / 2) * ((m + 1) / 2).
    // Dưới đây là cách tính trực tiếp:
    long long kq = ((n + 1) / 2) * ((m + 1) / 2);
    cout << kq;
    return 0;
}
  • Time Complexity: O(1)
  • Space Complexity: O(1)

Hầu hết các giải pháp accepted đều sử dụng cùng một công thức ngắn gọn. Logic đằng sau nó là chia nhỏ bài toán thành các chiều độc lập. Trong 1 chiều (ví dụ hàng), một viên đạn bắn tại hàng i sẽ che các hàng i-1, i, i+1. Để che liên tục, ta có thể đặt các viên đạn tại hàng 2, 5, 8,... (bước nhảy 3). Số lượng vị trí tối đa trong 1 chiều n là (n + 1) / 2. Tích của số lượng vị trí tối ưu cho chiều ngang và chiều dọc cho ra đáp án cuối cùng. Đây là cách tiếp cận hiệu quả nhất về mặt thời gian và bộ nhớ.

Phân tích độ phức tạp

Cách tiếp cận Time Space Tên
1 O(1) O(1) Tối ưu hóa vị trí bắn
2 O(1) O(1) Mô phỏng chia lưới (Bing Chilling)

Bài học kinh nghiệm

  • Mỗi viên đạn có bán kính bao phủ 1 đơn vị (tức là 3x3 ô).
  • Các viên đạn có thể được đặt chéo nhau để che phủ hoàn toàn lưới mà không cần quá nhiều.
  • Bài toán có thể quy về bài toán tìm số lượng vị trí đặt 'tâm' của các hình chữ nhật 3x3 sao cho che phủ hết lưới n×m.
  • Công thức tối ưu: ((n + 1) / 2) * ((m + 1) / 2).

Lỗi thường gặp

  • Sử dụng int thay vì long long cho n và m (vì n, m có thể lên tới 10^6, tích của chúng có thể tràn số 32-bit).
  • Hiểu sai cơ chế phá hủy: một số người nhầm là bắn trúng ô đó và 4 ô kề cạnh, nhưng thực tế là 9 ô (3x3).
  • Công thức (n/2 + 1) * (m/2 + 1) có thể sai lệch nhỏ so với (n+1)/2 * (m+1)/2 nếu chia số nguyên, nhưng (n+1)/2 là cách viết an toàn và chính xác nhất cho việc tính số lượng vị trí.

Bình luận

Please read the guidelines before commenting.


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