给你一个数列,对于每个数字你都可以++或者−−
然后花费就是你修改后和原数字的差值,然后问你修改成一个严格递增的,最小花费
如果不是严格递增的,那么就是很多数字可以取相同的
而且只有可能是出现的这n个数字的取值
为什么呢,如果你取的是两个值中间的一个没有出现的值
然后有x个值,修改成这个,肯定没有上下两个端点的值来的更优
所以这样考虑的话,对于不严格递增的要求
dp[i][j]=abs(b[i]−a[j])+min(dp[i−1][k]),k≤j
表示前i个数字,最后一个最大的是j,然后前i−1个,就选择k≤j并且花费最小的那个
O(n2)的状态,可以用前缀和优化O(1)的转移
蓝儿如何转化为不严格递增呢,ai−aj≥i−j,i≥j
ai−i≥aj−j
所以把ai=ai−i就能不严格递增的做了
直接考虑结果的整个数列 必然存在那么几个数 他是从始至终 都没有变过 那么假设有一些数会受到这个数影响 比如一个数ai不变 如果aj 是受ai影响 那么就会消耗
先考虑这样一个问题,如果是非严格单调递增该如何做,我们会发现每次调整,都是调整某个数字为原先数列中存在的数字,最后才是最优的,所以,我们设DP[i][j]表示前i个数字,最后一个数为原先数列排序后第j大的数字的最小代价,那么做一遍n2的DP就能够获得答案,现在题目中要求的是严格单调递增,那么就用到一种经典的处理方法,a[i]=a[i]-i,这样子就转化为非严格单调的问题了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 3e3 + 5;
const ll INF = 0x7fffffffffffffff; //这里不是const int 是 const ll
ll a[maxn], b[maxn], dp[maxn][maxn]; //因为每个数都在1e9范围内,所以要用ll
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
a[i] -= i; //这里就是把严格递增转换成非严格递增
b[i] = a[i];
}
sort(b+1, b+n+1);
for(int i = 1; i <= n; i++)
{
ll min1 = INF;
for(int j = 1; j <= n; j++)
{
min1 = min(min1, dp[i-1][j]); //这个min1表示的就是i-1个数并且最后一个数小于等于b[j]的最小值,这种记录方式很好
dp[i][j] = min1 + abs(a[i]-b[j]);
}
}
ll ans = INF;
for(int j = 1; j <= n; j++)
{
ans = min(dp[n][j], ans); //看哪个结尾最小
}
cout << ans << endl;
return 0;
}