题解 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;
}
posted @ 2021-09-14 14:03  recollector  阅读(58)  评论(0)    收藏  举报