分析
https://www.luogu.org/blog/FlierKing/solution-p2501
对于第二问的感性理解就是有上下两条线,一些点在上面的线的上面或者下面的线的下面,然后看它们变成哪个线的位置更优
代码
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cctype> #include<cmath> #include<cstdlib> #include<queue> #include<ctime> #include<vector> #include<set> #include<map> #include<stack> using namespace std; #define int long long const int inf = 1e9+7; int a[100100],d[100100],dp[100100],ddp[100100],sum1[100100],sum2[100100]; vector<int>v[100100]; signed main(){ int n,m,i,j,k,x,Ans=0; scanf("%lld",&n); for(i=1;i<=n;i++)scanf("%lld",&x),a[i]=x-i; a[++n]=inf;d[0]=a[0]=-inf; for(i=1;i<=n;i++)d[i]=inf; for(i=1;i<=n;i++){ int p=upper_bound(d,d+Ans+1,a[i])-d; Ans=max(Ans,p); dp[i]=p; d[p]=a[i]; } v[0].push_back(0); for(i=1;i<=n;i++)v[dp[i]].push_back(i); printf("%lld\n",n-Ans); ddp[0]=0; for(i=1;i<=n;i++)ddp[i]=inf; for(i=1;i<=n;i++) for(j=0;j<v[dp[i]-1].size();j++){ int to=v[dp[i]-1][j]; if(to>i)break; if(a[to]>a[i])continue; for(k=to;k<=i;k++)sum1[k]=abs(a[k]-a[to]),sum2[k]=abs(a[k]-a[i]); for(k=to+1;k<=i;k++)sum1[k]+=sum1[k-1],sum2[k]+=sum2[k-1]; for(k=to;k<i;k++) ddp[i]=min(ddp[i],ddp[to]+sum1[k]-sum1[to]+sum2[i]-sum2[k]); } printf("%lld\n",ddp[n]); return 0; }