题目链接
http://codeforces.com/problemset/problem/713/C
思路
a[i]非常大但是n很小,可以考虑离散化,将一维考虑成下标,那么可以先将数组排序成b[i]
有一个题目是求非严格的单调递增序列需要的代价:
状态表示:d[i][j], 第i个数为b[]中的第j个数需要的最小代价
转移方程:d[i][j] = min(d[i][j], d[i - 1][k] + |a[i] - b[j]|) k ≤ j;
其中依赖于这样一个性质:对一个数a[i],经过操作后一定是原序列中的某个数(可以考虑将若干个数转化为x,那么代价为|a[i] - x|,当x为这些数的中位数的时候代价最小,因此经过操作后一定是原序列中的某个数)
上面的问题解决了后,这道题要求的是一个严格递增的序列,考虑若a[i] < a[i + 1],那么有a[i] - i ≤ a[i + 1] - (i + 1)。即将每位数字减去下标,就转化成了非严格递增序列的最小代价问题
细节
本来状态是O(n^2),转移是O(n)的,但是实际上只要最小的d[i - 1][k],因此每次记录一下最小的d[i - 1][k]即可
代码
#include <iostream>
#include <cstring>
#include <stack>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <deque>
#include <bitset>
#include <algorithm>
using namespace std;
#define PI acos(-1.0)
#define LL long long
#define PII pair<int, int>
#define PLL pair<LL, LL>
#define mp make_pair
#define IN freopen("in.txt", "r", stdin)
#define OUT freopen("out.txt", "wb", stdout)
#define scan(x) scanf("%d", &x)
#define scan2(x, y) scanf("%d%d", &x, &y)
#define scan3(x, y, z) scanf("%d%d%d", &x, &y, &z)
#define sqr(x) (x) * (x)
const int maxn = 3005;
const LL INF = 1e18;
LL a[maxn], b[maxn], pre[maxn], d[maxn][maxn];
int n;
int main() {
scan(n);
for (int i = 1; i <= n; i++) {
scanf("%I64d\n", &a[i]);
a[i] -= i;
b[i] = a[i];
}
sort(b + 1, b + 1 + n);
for (int i = 0; i <= n; i++) {
pre[i] = INF;
for (int j = 0; j <= n; j++) {
d[i][j] = INF;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == 1) d[i][j] = min(d[i][j], abs(a[i] - b[j]));
else d[i][j] = min(d[i][j], pre[j] + abs(a[i] - b[j]));
pre[j] = min(pre[j - 1], d[i][j]);
}
}
LL res = 1e18;
for (int j = 1; j <= n; j++) res = min(res, d[n][j]);
printf("%I64d\n", res);
return 0;
}