Solution

题目链接

题目描述

现在我们有一个长度为 n的整数序列 a。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。但是不希望改变过多的数,也不希望改变的幅度太大。

输入格式

第一行是一个整数,表示序列长度 n
第二行有 n个整数,第 i个整数表示序列的第 iai

输出格式

第一行输出一个整数,表示最少需要改变多少个数。

第二行输出一个整数,表示在改变的数最少的情况下,每个数改变的绝对值之和的最小值。

输入输出样例

输入:4 5 2 3 5

输出:1 4


 

第一问

第一问题目非常easy

我们发现如果要同时保留a序列中a[i]与a[j]两个元素,则必须要满足 a[j]-a[i]≥j-i;

移项一下 我们发现 a[j]-j≥a[i]-i;

于是我们发现,我们可以开一个b数组其中对于任意元素b[i]=a[i]-i;

显然 这个b序列一定是不下降的;

于是最少改变多少个数,也就变成了要求b序列的最长不下降子序列,我们可以直接用dp来做复杂度 O(nlogn)

如果不明白我代码中写的话 请看这里

当然 也可以用树状数组来完成这一操作接下来请看第二问

第二问

第二问 我们发现一种情况是我们要解决的

每个被保留的b[i]和b[j]之间的均不合法,那么如何来改变b[i]与b[j]呢?

后面我来给个详细的证明吧 如果结合我的代码思考不出来的话,请看这位大佬的博客很详细

就是不断的缩减所谓的“台阶”,最后剩下左边的高b[i]与右边的高b[j]

最优解一定是(或者说,一定可以是)左边的b[i]到b[k]全部变成b[i]并且右边的b[k+1]到b[j]全部变成b[j]

如果最优解不是这样,我们可以无偿甚至减偿来变成这种形态

然后枚举每个区间的k就好了

code:

 

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define re register 
const int maxn=4e4+9,INF=0x3f3f3f3f;
int a[maxn],f[maxn],b[maxn],d[maxn],l[maxn],n,len=1,sum1[maxn],sum2[maxn];
vector<int> p[maxn];
signed main()
{
    scanf("%lld",&n);
    for(re 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(re 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);
    p[0].push_back(0);
    b[0]=-INF;b[n+1]=INF;
    memset(f,INF,sizeof f);
    f[0]=0;
    for(re int i=1;i<=n+1;i++)
    {
        for(re int h=0,size=p[l[i]-1].size();h<size;++h)
        {
            int j=p[l[i]-1][h];
            if(j>i||b[j]>b[i]) continue;
            sum1[j]=0;
            for(re int k=j+1;k<=i-1;k++)
                sum1[k]=sum1[k-1]+abs(b[k]-b[j]);
            sum2[i-1]=0;
            for(re int k=i-2;k>=j;k--)
                sum2[k]=sum2[k+1]+abs(b[k+1]-b[i]);
            for(re 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;
}