• [HAOI2006]数字序列 题解


    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-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;
    }

     

     

     

     

     
  • 相关阅读:
    一次访问ORACLE数据字典的优化
    利用分区优化SQL
    转行程序员的故事
    有一个苹果
    ubuntu10.04启动后出现grub rescue 模式
    source 命令 && . 命令
    ubuntu10.04启动后出现grub rescue 模式
    Android 各国语言缩写各国语言简称 .
    cpu的核心数、线程数、处理器的位数、操作系统的位数、能够支持最大内存 小结
    产品生产的各个阶段:DV,EV,PV ········是什么意思
  • 原文地址:https://www.cnblogs.com/yjyl0098/p/14133414.html
Copyright © 2020-2023  润新知