AcWing 273. 分級
題目
給一個序列 A,長度 n < 3e3。你需要構造出序列 B,且序列 B只能不增或者不降。求 S 的最小值。
分析
首先不增或者不降的序列做法都一樣,只需要反轉數組再做一遍求最小值即可。先假設求不降的序列。
題目關鍵(性質):一定存在一組最優解 B,B 序列的每一個元素都在 A 序列中出現過。
其實自己畫幾個樣例就能發現。
證明
先求出排序過的
①: 狀態表示(經驗)
- 集合: 表示所有給 分配好,且最後一位是 的所有方案集合
- 屬性:表示集合中所有方案求得結果的最小值
②: 狀態轉移
因爲最後一位已經確定,根據倒數第二位來劃分集合 (繼續劃分),枚舉 ( k <= j) ---->
複雜度
當然,這裏的 還是可以通過前綴最大值優化,變成 。
#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;
}