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; }
没有人不辛苦,只有人不喊疼