AcWing 273. 分級 (序列型DP + 前綴最值優化)

AcWing 273. 分級

題目

給一個序列 A,長度 n < 3e3。你需要構造出序列 B,且序列 B只能不增或者不降。求 S 的最小值。S=i=1NAiBiS=\sum_{i=1}^{N}\left|A_{i}-B_{i}\right|

分析

首先不增或者不降的序列做法都一樣,只需要反轉數組再做一遍求最小值即可。先假設求不降的序列。

題目關鍵(性質):一定存在一組最優解 B,B 序列的每一個元素都在 A 序列中出現過。
其實自己畫幾個樣例就能發現。
證明
先求出排序過的 AA'
①: 狀態表示(經驗)

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

②: 狀態轉移

因爲最後一位已經確定,根據倒數第二位來劃分集合 dp[i][j]dp[i][j](繼續劃分),枚舉 kk ( k <= j) ----> dp[i1][k]+(b[j]a[i])dp[i-1][k] + (b[j] - a[i])

複雜度 O(n3)O(n3)

當然,這裏的 kk 還是可以通過前綴最大值優化,變成 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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章