Hướng dẫn giải của [VTT Q4 Contest 04] Bài 1: Trò chơi biểu thức [EXP]


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.

Hướng dẫn giải

Cần đổi chỗ đúng một cặp vị trí trong dãy sao cho biểu thức đan dấu ~S = a_1 - a_2 + a_3 - \dots~ lớn nhất. Nhận xét then chốt: đổi chỗ hai vị trí cùng tính chẵn lẻ thì dấu của hai số hạng giống nhau nên ~S~ không đổi; chỉ đổi chỗ vị trí lẻ với vị trí chẵn mới làm ~S~ thay đổi.

Sub 1: Vét cạn — ~O(n^3)~, dùng khi ~n \le 100~

Thử mọi cặp ~(i, j)~, đổi chỗ, tính lại ~S~ từ đầu bằng một vòng lặp, lấy giá trị lớn nhất rồi đổi lại. Trường hợp ~n = 1~ không có phép đổi nào nên in luôn ~a_1~. Nếu chỉ nộp sub này: đúng 10/20 test (các test lớn bị quá giờ).

Sub 2: Công thức theo chẵn lẻ — ~O(n)~

Khi đổi chỗ vị trí lẻ ~i~ với vị trí chẵn ~j~, số hạng cũ là ~a_i - a_j~, số hạng mới là ~a_j - a_i~, nên ~S~ tăng thêm ~2(a_j - a_i)~. Muốn lợi nhất thì lấy ~a_j~ lớn nhất trong các vị trí chẵn và ~a_i~ nhỏ nhất trong các vị trí lẻ. Bẫy: nếu ~n \ge 3~ luôn có hai vị trí cùng tính chẵn lẻ để đổi mà ~S~ không đổi, nên độ tăng không bao giờ âm (lấy ~\max(0, \cdot)~); còn ~n = 2~ buộc phải đổi hai số nên độ tăng có thể âm; ~n = 1~ giữ nguyên. Dùng ~long long~. Nếu chỉ nộp sub này: đúng 20/20 test.

Chương trình hoàn chỉnh

Hàm main đọc dữ liệu; ~n \le 100~ gọi sub1, ngược lại gọi sub2. Khi đi thi nên viết như vậy: sub sau có sai hoặc chưa kịp làm thì vẫn ăn điểm sub trước.

#include <bits/stdc++.h>
using namespace std;

int n;
long long a[100005];

// Giá trị biểu thức S hiện tại
long long tinhS() {
    long long s = 0;
    for (int i = 1; i <= n; i++) s += (i % 2 == 1 ? a[i] : -a[i]);
    return s;
}

// Sub 1: thử mọi cặp (i, j), đổi chỗ, tính lại S — ~O(n^3)~ nên chỉ dùng khi n nhỏ
void sub1() {
    long long best = LLONG_MIN;
    if (n == 1) { cout << a[1] << "\n"; return; }
    for (int i = 1; i <= n; i++)
        for (int j = i + 1; j <= n; j++) {
            swap(a[i], a[j]);
            best = max(best, tinhS());
            swap(a[i], a[j]);
        }
    cout << best << "\n";
}

// Sub 2: chỉ đổi chỗ hai vị trí khác tính chẵn lẻ mới làm S thay đổi: lẻ i, chẵn j => S tăng 2*(a[j]-a[i]).
void sub2() {
    long long S = tinhS();
    if (n == 1) { cout << S << "\n"; return; }
    long long maxChan = LLONG_MIN, minLe = LLONG_MAX;
    for (int i = 1; i <= n; i++) {
        if (i % 2 == 0) maxChan = max(maxChan, a[i]);
        else minLe = min(minLe, a[i]);
    }
    long long gain = 2 * (maxChan - minLe);
    // n >= 3 luôn có hai vị trí cùng tính chẵn lẻ để đổi mà S không đổi => gain không âm.
    // n = 2 buộc phải đổi chỗ hai vị trí đó.
    if (n >= 3) gain = max(gain, 0LL);
    cout << S + gain << "\n";
}

int main() {
    freopen("EXP.INP", "r", stdin);
    freopen("EXP.OUT", "w", stdout);
    ios::sync_with_stdio(false); cin.tie(nullptr);
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    if (n <= 100) sub1();
    else sub2();
}

Bình luận

Hãy đọc nội quy trước khi bình luận.


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