Day6T1
容易想到一个dp:
记\(dp_{i,j,0}\)表示考虑到第\(i\)个数,其值为\(j\),正处于平地。
记\(dp_{i,j,1}\)表示考虑到第\(i\)个数,其值为\(j\),正处于上坡的中间(即不能再上升了)。
记\(dp_{i,j,2}\)表示考虑到第\(i\)个数,其值为\(j\),正处于下坡的中间(同理)。
转移的时候,平地只能从\(j\)相同的平地转移而来,上坡可以从\(j\)更小的平地或下坡转移,下坡同理。
具体实现时还需要将\(a\)离散化(这里不妨猜一下\(j\)只有可能取\(\{x|\exists i\in [1,n],x\in [a_i-1,a_i+1]\}\)中的值)。
代码如下,时间复杂度\(O(n^2)\):
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int a[N],b[N*3],tp;
long long dp[3][N*3],lmn[2][N*3],rmn[2][N*3];
void remn(){
lmn[0][0] = dp[0][0],lmn[1][0] = dp[2][0];
for(int j=1;j<=tp;j++) lmn[0][j] = min(lmn[0][j-1],dp[0][j]),lmn[1][j] = min(lmn[1][j-1],dp[2][j]);
rmn[0][tp] = dp[0][tp],rmn[1][tp] = dp[1][tp];
for(int j=tp-1;j>=0;j--) rmn[0][j] = min(rmn[0][j+1],dp[0][j]),rmn[1][j] = min(rmn[1][j+1],dp[1][j]);
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
b[i*3-2] = a[i]-1;
b[i*3-1] = a[i];
b[i*3] = a[i]+1;
}
sort(b+1,b+n*3+1);
tp = 1;
for(int i=2;i<=n*3;i++) if(b[i]!=b[i-1]) b[++tp] = b[i];
memset(dp,0x7f,sizeof(dp));
for(int i=0;i<=tp;i++) dp[0][i] = abs(a[1]-b[i]);
remn();
for(int i=2;i<=n;i++){
for(int j=0;j<=tp;j++){
if(j!=0){
dp[1][j] = min(dp[1][j],lmn[0][j-1]);
dp[1][j] = min(dp[1][j],lmn[1][j-1]);
}
if(j!=tp){
dp[2][j] = min(dp[2][j],rmn[0][j+1]);
dp[2][j] = min(dp[2][j],rmn[1][j+1]);
}
dp[0][j]+=abs(a[i]-b[j]);
dp[1][j]+=abs(a[i]-b[j]);
dp[2][j]+=abs(a[i]-b[j]);
}
remn();
}
cout<<min(rmn[1][0],min(lmn[0][tp],lmn[1][tp]));
return 0;
}
不妨再猜一下,考虑到第\(i\)个数时,\(j\)只可能取\(\{x|\exists j\in [\max\{j-2,1\},\min\{j+2,n\}],x\in [a_j-1,a_j+1]\}\)中的值,然后时间复杂度就变成了\(O(n)\)...
我想到一个绝妙的证明,但是这里太小,写不开。
其实就是写在代码后面了...
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
const int K = 2;
int a[N],tp;
long long dp[3][2][15];
vector<int> val[N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
for(int j=max(1,i-K);j<=min(n,i+K);j++) for(int l=-1;l<=1;l++) val[j].emplace_back(a[i]+l);
}
for(int i=1;i<=n;i++){
sort(val[i].begin(),val[i].end());
val[i].erase(unique(val[i].begin(),val[i].end()),val[i].end());
}
memset(dp,0x7f,sizeof(dp));
for(int i=0;i<val[1].size();i++) dp[0][1][i] = abs(a[1]-val[1][i]);
for(int i=2;i<=n;i++){
long long mn = 0x7f7f7f7f7f7f7f7fll;
for(int j=0,ptr=0;j<val[i].size();j++){
while(ptr<val[i-1].size()&&val[i-1][ptr]<val[i][j]){
mn = min(mn,dp[0][i&1^1][ptr]);
mn = min(mn,dp[2][i&1^1][ptr++]);
}
if(val[i-1][ptr]==val[i][j]){
dp[1][i&1][j] = min(mn,dp[1][i&1^1][ptr])+abs(a[i]-val[i][j]);
dp[0][i&1][j] = dp[0][i&1^1][ptr]+abs(a[i]-val[i][j]);
}
else{
dp[0][i&1][j] = 0x7f7f7f7f7f7f7f7fll;
dp[1][i&1][j] = mn+abs(a[i]-val[i][j]);
}
}
mn = 0x7f7f7f7f7f7f7f7fll;
for(int j=val[i].size()-1,ptr=val[i-1].size()-1;j>=0;j--){
while(ptr>=0&&val[i-1][ptr]>val[i][j]){
mn = min(mn,dp[0][i&1^1][ptr]);
mn = min(mn,dp[1][i&1^1][ptr--]);
}
if(val[i-1][ptr]==val[i][j]) dp[2][i&1][j] = min(mn,dp[2][i&1^1][ptr])+abs(a[i]-val[i][j]);
else dp[2][i&1][j] = mn+abs(a[i]-val[i][j]);
}
}
long long ans = 0x7f7f7f7f7f7f7f7fll;
for(int i=0;i<val[n].size();i++) for(int k=0;k<3;k++) ans = min(ans,dp[k][n&1][i]);
cout<<ans;
return 0;
}
这里只证明第二个猜测,容易发现其是第一个猜测的充分条件。

浙公网安备 33010602011771号