CF713C Sonya and Problem Wihtout a Legend (经典dp)

一个经典的通过增长减小大小来求使得序列单调性的最小代价。

对于这道题,有一个前置题是不要求要严格单调,而是不严格单调

这样的话,我们可以得到一个性质,最后所有的值都是在原序列当中的,这其实用贪心的想法想一想就好,因为一旦通过加减与左边或右边相等,就没必要再加减了

但是本题要求严格,这就很难说了,因此要考虑将一个问题转化成已知的问题

对于原先的问题,其实就是a[i]-a[j]>=0就好

那么现在的问题是a[i]-a[j]>=i-j,因此我们只要对输入的原数列减下标i,就转化成上面的问题了

也就是 a[i]-i>=a[j]-j。

对于dp问题,要进行集合划分,而对于这样的数值问题,以及这样的数据范围为,自然考虑到二维状态,第一维自然是前i个,重点是第二维,我们一般要寻找题目的信息

而第二维的思考就是考虑如何能将所有状态划分,一般来说都可以考虑最后一位不同的不同情况。因此,这次我们可以设计为前i个,第i个的值是j的最小值,因为根据上文所说,j永远都是原数列中的值

但是这样的话需要多枚举一层,因为我们要到前面找不同的k,1<=k<=j,才能完整的求,所以又出现了一个常见的优化,我们将dp状态改为前i个,第i个最大是j的情况。这样就可以通过前缀优化

本题要离散化,且爆int

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
#include<algorithm>
#include<queue>
#include<set>
#define ull unsigned long long
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int N=1e5+10;
ll f[3010][3010];
vector<ll> num;
ll a[N];
int main(){
    int n;
    cin>>n;
    int i;
    for(i=1;i<=n;i++){
        cin>>a[i];
        a[i]-=i;
        num.push_back(a[i]);
    }
    memset(f,0x3f,sizeof f);
    sort(num.begin(),num.end());
    num.erase(unique(num.begin(),num.end()),num.end());
    int cnt=num.size();
    for(i=1;i<=cnt;i++){
        f[1][i]=abs(a[1]-num[i-1]);
        if(i>1){
            f[1][i]=min(f[1][i],f[1][i-1]);
        }
    }
    int j;
    for(i=2;i<=n;i++){
        for(j=1;j<=cnt;j++){
            f[i][j]=min(f[i][j-1],f[i-1][j]+abs(a[i]-num[j-1]));
        }
    }
    cout<<f[n][cnt]<<endl;
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/ctyakwf/p/12638176.html