先上题目:
给定长度为 N 的序列 A,构造一个长度为 N 的序列 B,满足:
1. B 非严格单调,即 B1≤B2≤…≤BN或 B1≥B2≥…≥BN。
2. 最小化 S=∑Ni=1|Ai−Bi|
只需要求出这个最小值 S。
输入格式
第一行包含一个整数 N。
接下来 N 行,每行包含一个整数 Ai。
输出格式
输出一个整数,表示最小 S 值。
数据范围
1≤N≤2000 ; 0≤Ai≤10^6
分析:
首先,要先获得一条性质:所有的B[i]都能在A[i]中找到。(反正我并没有觉得很显然,我是想不到这东西的,刚开始往平均数那里去想了)
证明这个性质吧:
先画出这个图,然后想啊,现在考虑没有在线上的圈圈,怎么把他们挪动线上呢?当A[k](表示粉色框框里面的圈圈所对应的A[i])小于A`[i](在这里指A`[1])的个数大于 大于A`[i+1](在这里指A`[2])的个数时,向下移动,这样,整体来看,差距应该是变小的。反之则向上移动。这样,在有限次的移动下,所有的圆圈都能移到线上,而且差距一定逐渐变小。
然后就要用的dp了,假设dp[i][j]表示的含义是第i个元素为A`[j]时,S的最小值。所以:
#include<iostream>
#include<algorithm>
using namespace std;
const int INF = 0x3f3f3f3f, N = 2005;
int a[N], b[N], n, dp[N][N];
int work()
{
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 vmin = INF;
for (int j = 1; j <= n; j++)//当i一定时,循环,得出最后第i个元素取不同值时的最小值
{
vmin = min(vmin, dp[i - 1][j]);//这里可以将i-1时的最小情况得出来,从而运用到i时。
dp[i][j] = vmin + abs(a[i] - b[j]);
}
}
int end = INF;
for (int i = 1; i <= n; i++)end = min(end, dp[n][i]);//遍历,寻找j为几时,s最小
return end;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
int summary = work();
reverse(a + 1, a + n + 1);//翻转过来,当B[i]递减时
summary = min(summary, work());//取递增递减两种情况的最小值。
cout << summary << endl;
return 0;
}
第一次真正意义上做DP,真的很难想到啊,要这样构造,主要是不知道怎样创造规则,dp一般就是运用一个二维数组,然后在里面再进行操作,主要是自己要找到规则。 其次就是这种题自己要找性质,想偏了就很难做出来