Codeforces Round 598 (Div.3 )

Codeforces Round 598 (Div. 3)

E. Yet Another Division Into Teams

\(Tutorial:\)

显然我们可以先将原数组 \(a\) 按权值从小到大排序,问题就变成了把数组划分成连续的几组,并记录方案

这是一个经典的 \(dp\) 问题,我们可以设 \(dp_i\) 表示把前 \(i\) 个数划分成若干个组 (每组长度至少是 \(3\)) 的最小价值和

我们可以得到转移方程:

\(dp_i =min_{j=1}^{i-2}\{dp_{j-1}+a_i-a_j\}\)

直接转移复杂度显然是 \(O(n^2)\)的,但我们可以考虑分离 \(a_i\) ,发现可以利用前缀最小值优化掉这个\(dp\)

\(dp_i=a_i+min_{j=1}^{i-2}\{dp_{j-1}-a_j\}\)


\(Code:\)

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

typedef long long LL;

const int N = 2e5 + 10;
int a[N], b[N], f[N], ans[N];
LL dp[N], pre[N];

void solve() {

    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        b[i] = i;
    }

    sort(b + 1, b + n + 1, [&](int x, int y){
        return a[x] < a[y];
    });

    sort(a + 1, a + n + 1);

    for (int i = 0; i <= n; i++) {
        dp[i] = 1e18;
    }

    dp[0] = 0;
    pre[0] = 0;

    for (int i = 1; i <= n; i++) {
        if (i >= 3) {
            dp[i] = dp[pre[i - 3]] + a[i] - a[pre[i - 3] + 1];
            f[i] = pre[i - 3];
        }
        if (dp[i] - a[i + 1] < dp[pre[i - 1]] - a[pre[i - 1] + 1]) {
            pre[i] = i;
        } else pre[i] = pre[i - 1];
    }


    int ed = n;
    int color = 0;
    while (ed) {
        color++;
        for (int i = f[ed] + 1; i <= ed; i++) {
            ans[b[i]] = color;
        }
        ed = f[ed];
    }

    cout<< dp[n] << ' ' << color << '\n';

    for (int i = 1; i <= n; i++) {
        cout << ans[i] << " \n" [i == n];
    }

}

int main() {

    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    //cin >> t;
    t = 1;
    while (t--) {
        solve();
    }    

    return 0;
}
posted @ 2023-06-14 00:25  jackle  阅读(5)  评论(0编辑  收藏  举报