1. 程式人生 > 實用技巧 >AcWing 273 分級 (dp)

AcWing 273 分級 (dp)

https://www.acwing.com/problem/content/275/

可以用歸納法證明一定存在最優方案, \(B\) 中的數全在 \(A\) 中出現過

所以設 \(dp[i][j]\),表示已經構造好前 \(i\) 個數,第 \(i\) 個數為 \(j\) 的方案數,

\[dp[i][j] = max_{1\leq k \leq j} \{dp[i-1][k] + \mid A_i - j \mid \} \]

直接轉移是 \(O(n^3)\) 的,而我們發現,第三層的轉移中,決策集合是不會減少的,所以記錄下字首/字尾最小值轉移即可

因為 \(B\) 既可能是遞增的也可能是遞減的,所以做兩遍即可

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;

const int maxn = 2010;

int n, q;
int a[maxn], c[maxn], dp[maxn][maxn];

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	n = read();
	for(int i = 1 ; i <= n ; ++i) a[i] = read(), c[i] = a[i];
	
	sort(c + 1, c + 1 + n);
	q = unique(c + 1, c + 1 + n) - c - 1;
	for(int i = 1 ; i <= n ; ++i) a[i] = lower_bound(c + 1, c + 1 + n, a[i]) - c;
	
	dp[0][0] = 0;
	for(int i = 1 ; i <= n ; ++i){
		int mn = 1000000007;
		for(int j = 1 ; j <= q ; ++j){
			mn = min(mn, dp[i - 1][j]);
			dp[i][j] = mn + abs(c[j] - c[a[i]]);
		}
	}

	int ans = 1000000007;
	for(int i = 1 ; i <= q ; ++i)
		ans = min(ans, dp[n][i]);
	
	dp[0][0] = 0;
	for(int i = 1 ; i <= n ; ++i){
		int mn = 1000000007;
		for(int j = q ; j >= 1 ; --j){
			mn = min(mn, dp[i - 1][j]);
			dp[i][j] = mn + abs(c[j] - c[a[i]]);
		}
	}
	
	for(int i = 1 ; i <= q ; ++i)
		ans = min(ans, dp[n][i]);
	
	printf("%d\n", ans);
	
	return 0;
}