AcWing 273. 分级 (序列型DP + 前缀最值优化)

12 篇文章 0 订阅
9 篇文章 0 订阅

AcWing 273. 分级

题目

给一个序列 A,长度 n < 3e3。你需要构造出序列 B,且序列 B只能不增或者不降。求 S 的最小值。 S = ∑ i = 1 N ∣ A i − B i ∣ S=\sum_{i=1}^{N}\left|A_{i}-B_{i}\right| S=i=1NAiBi

分析

首先不增或者不降的序列做法都一样,只需要反转数组再做一遍求最小值即可。先假设求不降的序列。

题目关键(性质):一定存在一组最优解 B,B 序列的每一个元素都在 A 序列中出现过。
其实自己画几个样例就能发现。
证明
先求出排序过的 A ′ A' A
①: 状态表示(经验)

  1. 集合: d p [ i ] [ j ] dp[i][j] dp[i][j] 表示所有 A [ 1... i ] A[1...i] A[1...i]分配好,且最后一位是 A ′ [ j ] A'[j] A[j] 的所有方案集合
  2. 属性:表示集合中所有方案求得结果的最小值

②: 状态转移

因为最后一位已经确定,根据倒数第二位来划分集合 d p [ i ] [ j ] dp[i][j] dp[i][j](继续划分),枚举 k k k ( k <= j) ----> d p [ i − 1 ] [ k ] + ( b [ j ] − a [ i ] ) dp[i-1][k] + (b[j] - a[i]) dp[i1][k]+(b[j]a[i])

复杂度 O ( n 3 ) O(n3) O(n3)

当然,这里的 k k k 还是可以通过前缀最大值优化,变成 O ( n 2 ) O(n2) O(n2)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f;
const int N = 2e3 + 5;

int n, a[N], b[N];
int dp[N][N];

int DP() {
	for (int i = 1; i <= n; i++) 
		b[i] = a[i];
	sort(b + 1, b + n + 1);
	for (int i = 1; i <= n; i++) {
		int minv = INF;
		for (int j = 1; j <= n; j++) {
			minv = min(minv, dp[i-1][j]);	// 记录前缀最值
			dp[i][j] = minv + abs(b[j] - a[i]);
		}
	}
	int ans = INF;
	for (int i = 1; i <= n; i++) 
		ans = min(ans, dp[n][i]);
	return ans;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) 
		scanf("%d", &a[i]);
	int res = DP();
	reverse(a + 1, a + n + 1);
	res = min(res, DP());		// 求两次取最小
	printf("%d\n", res);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值