codeforces C. Sonya and Problem Wihtout a Legend(dp or 思维)

题目链接:http://codeforces.com/contest/713/problem/C

题解:这题也算是挺经典的题目了,这里附上3种解法优化程度层层递进,还有这里a[i]-i<=a[i+1]-(i+1),处理一下。

首先是最基础的dp[i][j]前i位最大值为j的最小值为多少。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define inf 0X3f3f3f3f
using namespace std;
const int M = 3e3 + 10;
typedef long long ll;
ll dp[M][M] , a[M] , b[M];
int main() {
    int n;
    scanf("%d" , &n);
    memset(b , 0 , sizeof(b));
    for(int i = 1 ; i <= n ; i++) scanf("%lld" , &a[i]) , b[i] = a[i] = a[i] - i;
    memset(dp , inf , sizeof(dp));
    int cnt = 0;
    b[0] = -1000000000000000;
    sort(b + 1 , b + 1 + n);
    for(int i = 1 ; i <= n ; i++) {
        if(b[i] != b[i - 1]) b[++cnt] = b[i];
    }
    for(int i = 1 ; i <= cnt ; i++) dp[0][i] = 0;
    for(int i = 1 ; i <= n ; i++) {
        ll tmp = dp[i - 1][1];
        for(int j = 1 ; j <= cnt ; j++) {
            tmp = min(tmp , dp[i - 1][j]);
            dp[i][j] = tmp + abs(a[i] - b[j]);
        }
    }
    ll ans = 1000000000000000;
    for(int i = 1 ; i <= cnt ; i++) {
        ans = min(ans , dp[n][i]);
    }
    printf("%lld
" , ans);
    return 0;
}

然后是滚动优化注意那个dp的转移方式只和上一个状态有关系所以可以用滚动优化dp[2][M],这样优化了空间

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define inf 0X3f3f3f3f
using namespace std;
typedef long long ll;
const int M = 3e3 + 10;
int a[M] , b[M];
ll dp[2][M];
int main() {
    int n , m;
    scanf("%d" , &n);
    for(int i = 1 ; i <= n ; i++) scanf("%d" , &a[i]) , a[i] -= i , b[i] = a[i];
    m = 0;
    sort(b + 1 , b + 1 + n);
    b[0] = -1e9 - 10;
    for(int i = 1 ; i <= n ; i++) {
        if(b[i] != b[i - 1]) b[++m] = b[i];
    }
    memset(dp , inf , sizeof(dp));
    for(int i = 1 ; i <= n ; i++) dp[0][i] = 0;
    for(int i = 1 ; i <= n ; i++) {
        ll tmp = dp[(i - 1) % 2][1];
        for(int j = 1 ; j <= m ; j++) {
            tmp = min(tmp , dp[(i - 1) % 2][j]);
            dp[i % 2][j] = tmp + abs(a[i] - b[j]);
        }
    }
    ll Min = 1e15 + 10;
    for(int i = 1 ; i <= m ; i++) Min = min(Min , dp[n % 2][i]);
    printf("%lld
" , Min);
    return 0;
}

最后是最强的优化复杂度可以到达O(nlogn),就是用优先队列来优化,其实这个具体去证明也不太好证明但是理解还是好理解的具体画一下图意会一下

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
priority_queue<int>q;
int main() {
    int n;
    cin >> n;
    long long ans = 0;
    for(int i = 1 ; i <= n ; i++) {
        int x;
        scanf("%d" , &x);
        x -= i;
        q.push(x);
        if(q.top() > x) {
            int t = q.top();
            q.pop();
            ans += t - x;
            q.push(x);
        }
    }
    cout << ans << endl;
    return 0;
}