携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情
[HAOI2006]数字序列
题目描述
现在我们有一个长度为 的整数序列 。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。但是不希望改变过多的数,也不希望改变的幅度太大。
输入格式
第一行是一个整数,表示序列长度 。
第二行有 个整数,第 个整数表示序列的第 项 。
输出格式
第一行输出一个整数,表示最少需要改变多少个数。
第二行输出一个整数,表示在改变的数最少的情况下,每个数改变的绝对值之和的最小值。
样例 #1
样例输入 #1
4
5 2 3 5
样例输出 #1
1
4
提示
数据规模与约定
- 对于 的数据,保证 。
- 对于 的数据,保证 ,。数据保证 随机生成。
我们假设 i,j,(j>i) 分别为两个不被修改的相邻点,那么他们之间所有点必然要么高于 jj 要么低于 ii。我们先对每个点分析,如果这个点高于 jj 我们最少把他移动到 jj 一样的高度就好了,这对他而言是最短距离。如果这个点低于 ii 那么我最少把他移动到 ii 一样的高度就好了,这对他而言是最短距离。不管实际应该怎么移动,实际移动一定是在这个移动的基础上再移动。
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
#define int long long
const int N=4e4+9,INF=0x3f3f3f3f;
int a[N],f[N],b[N],d[N],l[N],n,len=1,sum1[N],sum2[N];
vector<int> p[N];
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),b[i]=a[i]-i;
d[1]=b[1];b[n+1]=INF;
l[1]=1;
p[1].push_back(1);
for(int i=2;i<=n+1;i++)
{
if(d[len]<=b[i])
{
d[++len]=b[i];
l[i]=len;
p[len].push_back(i);
}
else
{
int pos=upper_bound(d+1,d+1+len,b[i])-d;
d[pos]=b[i];
l[i]=pos;
p[pos].push_back(i);
}
}
printf("%lld\n",n-len+1);//0 1 1 2 3 4
p[0].push_back(0);
b[0]=-INF;b[n+1]=INF;
memset(f,INF,sizeof f);
f[0]=0;
for(int i=1;i<=n+1;i++)
{
for(auto j:p[l[i]-1])
{//0 0 2 1 3 4
// printf("%lld ",j);
if(j>i||b[j]>b[i]) continue;
sum1[j]=0;
for(int k=j+1;k<=i-1;k++)
sum1[k]=sum1[k-1]+abs(b[k]-b[j]);
sum2[i-1]=0;
for(int k=i-2;k>=j;k--)
sum2[k]=sum2[k+1]+abs(b[k+1]-b[i]);
for(int k=j;k<=i-1;k++)
f[i]=min(f[i],f[j]+sum1[k]+sum2[k]);
}
}
printf("%lld",f[n+1]);
return 0;
}