Hướng dẫn giải của [Quảng Trị - TST - 2021] Bài 2: Phát quà


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.

Subtask 1. có tối đa một vị trí ~a_i < 0~

Với subtask này, dãy được chia làm ba phần:

  • Từ ~a_1~ tới ~a_{i - 1}~ có tổng không âm.
  • ~a_i~ có tổng âm.
  • Từ ~a_{i + 1}~ tới ~a_n~ có tổng không âm.

Nếu ~K \ge 2~, đương nhiên đáp án là tổng cả dãy, trừ đi phần tử âm đó.

Nếu ~K = 1~, ta có thể chọn phần đầu tiên, phần thứ ba, hoặc cả dãy.

Độ phức tạp: ~O~ (~n~).

Subtask 2. ~k = 1~

Đây là bài toán tìm dãy con có tổng lớn nhất. Bài toán này đã khá quen thuộc, có thể giải bằng mảng cộng dồn.

Độ phức tạp: ~O~ (~n~).

Subtask 3 + 4. ~n, k \le 300~

Ta sẽ quy hoạch động.

Gọi ~f[i][j]~ là tổng lớn nhất khi ta đã chọn ~i~ dãy con, và dãy con cuối cùng đang kết thúc tại ~j~.

Ta có công thức truy hồi: ~f[i][j] = max (f[i - 1][k] + ps[i] - ps[l])~, với ~ps[i]~ là mảng cộng dồn, ~1 \le k < i~ và ~k \le l < i~.

~-ps[j]~ thể hiện ta chọn đoạn con ~[l, i]~.

Độ phức tạp: ~O~ (~n^3~).

Subtask 5. ~n, k \le 2000~

Ta sử dụng prefix min để tối ưu công thức quy hoạch động trên.

Độ phức tạp: ~O~ (~n^2~).

Code mẫu:

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

#define int long long
const int N = 1e6 + 9;

int n, a[N], k;

namespace sub1 {
    bool valid() {
        int ret = 0;
        for (int i = 1; i <= n; i++) {
            if (a[i] < 0) ++ret;
        }
        return ret <= 1;
    }

    void solve() {
        int sum1 = 0, sum2 = 0;
        bool ok = 0;
        for (int i = 1; i <= n; i++) {
            if (a[i] < 0) {
                ok = 1; 
                continue;
            }
            if (!ok) sum1 += a[i];
            if (ok) sum2 += a[i];
        }
        if (k == 1) {
            cout << max(sum1, sum2);
        }
        else cout << sum1 + sum2;
        exit(0);
    }
}

namespace sub2 {
    bool valid() {
        return k == 1;
    }

    void solve() {
        int mx = 0, ans = 0;
        for (int i = 1; i <= n; i++) {
            mx = max(mx + a[i], a[i]);
            ans = max(ans, mx);
        }
        cout << ans;
        exit(0);
    }
}

namespace subfull {
    int pfs[2002];
    int dp[2002][2002];
    int prefix[2002];

    void solve() {
        for (int i = 1; i <= n; i++) {
            pfs[i] = pfs[i - 1] + a[i];
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= k; j++) {
                dp[i][j] = max(dp[i - 1][j], prefix[j - 1] + pfs[i]);
            }
            for (int j = 0; j <= k; j++) {
                prefix[j] = max(prefix[j], dp[i][j] - pfs[i]);
            }
        }
        cout << dp[n][k];
        exit(0);
    }
}
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
//  freopen("phatqua.inp", "r", stdin);
//  freopen("phatqua.out", "w", stdout);
    cin >> n >> k;
    for (int i = 1; i <= n; i++) cin >> a[i];
    if (sub1::valid()) sub1::solve();
    if (sub2::valid()) sub2::solve();
    subfull::solve();
    return 0;
}

// <33

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.