题解 CF13C Sequence
给定一个长度为 \(n\) 的序列 \(a\),每次操作可以把某个数 \(+1\) 或者 \(-1\)。要求把序列变成非降数列。而且要求修改后的数列只能出现修改前的数,求最少操作次数。
\(1 \leq n \leq 5\times 10^3 ,0 \leq a_i \leq 10^9\)。
Solution
设 \(b\) 为 \(a\) ,从小到大排序后的结果。
设 \(f_{i,j}\) 表示把 \(a_i\sim a_i\) 变成不降序列并且 \(a_i\) 最后变成 \(b_j\) 的最小代价。
\[f_{i,j}=|a_i-b_j|+\min_{1\leq k\leq j}\{f_{i-1,k}\}
\]
时间复杂度 \(\mathcal{O}(n^3)\) ,考虑优化。
发现后面的 \(\min\) 是一个前缀的形式,那么可以直接维护出每一个 \(f_i\) 的前缀最小值,就可以 \(\mathcal{O}(1)\) 转移。
时间复杂度 \(\mathcal{O}(n^2)\) ,可以通过。
注意直接开数组会爆空间,但是因为 \(f_i\) 之和 \(f_{i-1}\) 有关系,所以可以滚动数组优化。
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <iostream>
#include <queue>
typedef long long LL;
using namespace std;
inline int read() {
int num = 0 ,f = 1; char c = getchar();
while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
return num * f;
}
inline LL abs(LL x) {return x < 0 ? -x : x;}
const int N = 5005;
const LL INF = 0x3f3f3f3f3f3f3f3f;
LL f[2][N];
int a[N] ,b[N] ,n;
signed main() {
n = read();
for (int i = 1; i <= n; i++) a[i] = b[i] = read();
sort(b + 1 ,b + n + 1);
int now = 1;
f[0][0] = INF;
for (int i = 1; i <= n; i++ ,now ^= 1) {
f[now][0] = INF;
for (int j = 1; j <= n; j++)
f[now][j] = min(f[now][j - 1] ,abs(a[i] - b[j]) + f[now ^ 1][j]);
}
printf("%lld\n" ,f[now ^ 1][n]);
return 0;
}

浙公网安备 33010602011771号