CF1256E. Yet Another Division Into Teams 题解 单调队列优化dp
题目链接:https://codeforces.com/contest/1256/problem/E
将 \(n\) 个数分成若干队,每一队至少包含 \(3\) 个整数,每一队的代价是最大值与最小值只差,求所有队伍代价之和的最小值,以及分成的队伍的数量,以及每个数所在的队的编号。
解题思路:
就是比较简单的单调队列优化dp,主要是下面这个公式:
f[i] = min(f[j] - a[j+1]) + a[i]
但是因为还有一些额外数据要输出所以再加一些操作这道题目就解决了。
示例程序:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int n, p[maxn], team[maxn], sz[maxn], pres[maxn];
long long f[maxn];
deque<int> que;
struct Node {
int a, p;
} a[maxn];
bool cmp(Node a, Node b) {
return a.a < b.a;
}
// f[i] = min(f[j] - a[j+1]) + a[i]
long long cal(int p) {
return f[p] - a[p+1].a;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].a;
a[i].p = i;
}
sort(a+1, a+1+n, cmp);
for (int i = 3; i <= n; i++) {
int pre = i - 3;
if (pre != 1 && pre != 2) {
while (!que.empty() && cal(que.back()) >= cal(pre)) que.pop_back();
que.push_back(pre);
}
pres[i] = que.front();
f[i] = cal(que.front()) + a[i].a;
sz[i] = sz[que.front()] + 1;
}
for (int i = n; i; i = pres[i]) {
for (int j = pres[i]+1; j <= i; j++)
team[a[j].p] = sz[i];
}
cout << f[n] << " " << sz[n] << endl;
for (int i = 1; i <= n; i++) cout << team[i] << " ";
return 0;
}