273. 分级
给定长度为 N 的序列 A,构造一个长度为 N 的序列 B,满足:
只需要求出这个最小值 S。
第一行包含一个整数 N。
接下来 N 行,每行包含一个整数 Ai。
输出一个整数,表示最小 S 值。
1≤N≤2000
0≤Ai≤106
- 7
- 1
- 3
- 2
- 4
- 5
- 3
- 9
3
这是一道结论题,在做这道题之前先要知道一个结论:
在满足S最小化的前提下,一定会存在一种构造序列B的方案,使得B中的数值都在A中出现过
所以我们可以构造一种B序列,使得B序列的数从排过序的A中选
DP的核心思想是用集合来表示一类方案,然后从集合的维度来考虑状态之间的递推关系。
受上述性质启发,状态表示为:
f[i][j]:前 i 个A 中,第 i 个A与排序后的第 j 个A中选取第 j 个相减所得的最小值
可以发现这是一个不重不漏的集合划分方式
状态转移方程为:
f[i][j]=min(f[i-1][j-k])+abs(A[i]-SA[j])
初始化为 f =0x3f , f[0]=0
i: 1到n
j:1到n
k: 1到j
这显然会超时,所以我们优化一下
将状态转移方程展开:
f[i][j]=min(f[i-1][1],f[i-1][2],f[i-1][3]......f[i-1][j])+abs(A[i]-SA[j])
所以我们可以用前缀处理的思想处理:min(f[i-1][1],f[i-1][2],f[i-1][3]......f[i-1][j])
- for (int i = 1; i <= n; i++) {
- mn = 0x3f3f3f3f;
- for (int j = 0; j <= n; j++) {
- mn = min(mn, f[i - 1][j]);
- f[i][j] = mn + abs(a[i] - sa[j]);
- }
- }
所以代码就为
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- using namespace std;
- typedef long long LL;
- const int N = 2000 + 5;
- int n;
- int a[N],sa[N],f[N][N];
-
- int cmp(const int& a, const int& b) {
- return a > b;
- }
-
- int main() {
- scanf("%d", &n);
- for (int i = 1; i <= n; i++) {
- scanf("%d", &a[i]);
- sa[i] = a[i];
- }
- sort(sa + 1, sa + 1 + n);
- memset(f, 0x3f, sizeof(f));
- memset(f[0], 0, sizeof(f[0]));
- f[0][0] = 0;
- int mn = 0x3f3f3f3f;
- for (int i = 1; i <= n; i++) {
- mn = 0x3f3f3f3f;
- for (int j = 0; j <= n; j++) {
- mn = min(mn, f[i - 1][j]);
- f[i][j] = mn + abs(a[i] - sa[j]);
- }
- }
- int ans = 0x3f3f3f3f;
- for (int i = 1; i <= n; i++) {
- ans = min(ans, f[n][i]);
- }
- memset(f, 0x3f, sizeof(f));
- memset(f[0], 0, sizeof(f[0]));
- sort(sa + 1, sa + 1 + n, cmp);
- for (int i = 1; i <= n; i++) {
- mn = 0x3f3f3f3f;
- for (int j = 1; j <= n; j++) {
- mn = min(mn, f[i - 1][j]);
- f[i][j] = mn + abs(a[i] - sa[j]);
- }
- }
- for (int i = 1; i <= n; i++) {
- ans = min(ans, f[n][i]);
- }
- cout << ans << endl;
- return 0;
- }
-
详细的证明:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- using namespace std;
- typedef long long LL;
- const int N = 2e3 + 5;
- int n;
- int a[N], b[N];
- int f[N][N];
-
- int cmp(const int& a, const int& b) {
- return a < b;
- }
-
- int dp() {
- for (int i = 1; i <= n; i++)
- b[i] = a[i];
- sort(b + 1, b + 1 + n, cmp);
-
- for (int i = 1; i <= n; i++) {
- int minv = 0x3f3f3f3f;
- for (int j = 1; j <= n; j++) {
- minv = min(minv, f[i-1][j]);
- f[i][j] = minv + abs(a[i] - b[j]);
- }
- }
- int ret = 0x3f3f3f3f;
- for (int i = 1; i <= n; i++)
- ret = min(ret, f[n][i]);
- return ret;
- }
-
- int main() {
- cin >> n;
- for (int i = 1; i <= n; i++)
- scanf("%d", &a[i]);
- int ans = dp();
- reverse(a + 1, a + 1 + n);
- ans = min(ans, dp());
- cout << ans << endl;
- return 0;
- }