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;
}