zoukankan      html  css  js  c++  java
  • poj3666/CF714E/hdu5256/BZOJ1367(???) Making the Grade[线性DP+离散化]

    给个$n<=2000$长度数列,可以把每个数改为另一个数代价是两数之差的绝对值。求把它改为单调不增or不减序列最小代价。


    话说这题其实是一个结论题。。找到结论应该就很好做了呢。

    手玩的时候就有感觉,改造出来的数列的元素会不会全是原来数列里有的数?弄了几组发现没问题,但是还是踟蹰不前,不敢下手。。然后我就智障的换思路了。。。这个故事告诉我们发现一个暂时没找到反例的结论一定要大胆实践反正交到OJ上不要钱

    所以这题结论就上面那个。具体证明呢。。我不会。。。

    然后就简单了啊。有个很好想的状态$f[i][j]$第$i$位填数字$j$(因为$j$是只在原数组中出现的,所以可以离散化压缩状态为2000个)。于是每次枚举之,在上一位中找小于当前值的转移过来。

    $f[i][j]=min(f[i-1][k]+|A[j]-a[i]|)$       $1<=k<=j$

    然后k那一维做个前缀max减一维枚举就n方啦。

    Upd:补证明。其实lyd书上是有的,但是并没看懂。后来由于thx学长帮助,大概理解了(%%%)。

    数学归纳法。

    对于$n=1$,显然成立。

    对于$n=k-1$时假设其满足由原数列的数构成最优解。那么,考虑对于$n=k$。

    当$A_n ≥ B_{n-1}$时(B为之后的数列)显然直接用$A_n$最优。

    当$A_n ≤ B_{n-1}$时有两种选择一种是由上一个$B_{n-1}$继续过来。

    一种是把当前$B_i$向下调整向下调整时,前面的构造好的数列要被迫下调。

    那么假设下调至$x$。前面连着的一段应当也调为$x$。

    (到这里都是lyd原话)

    为什么统一调为$x$,前面构造好的数为什么不会变的比$x$更小从而更优?

    如果之前的变得更小就更优的话,那我在之前就应当将它改为更小值保证最优了,而不是现在。

    也就是说只能统一改为一个数$x$。

    这时候用结论,这一段每个数与$x$差的绝对值要最小化,做多了就知道这是个中位数问题。

    那么取这段数中的$mid$一定是可以保证代价最小的。

    综上,$B_n$在保证最优的情况下只可能用A数列中有的数。

    Upd2:见code下面。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm>
     6 #include<queue>
     7 #define dbg(x) cerr<<#x<<" = "<<x<<endl
     8 #define ddbg(x,y) cerr<<#x<<" = "<<x<<"   "<<#y<<" = "<<y<<endl
     9 using namespace std;
    10 typedef long long ll;
    11 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
    12 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
    13 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
    14 template<typename T>inline T _abs(T A){return A<0?-A:A;}
    15 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
    16 template<typename T>inline T read(T&x){
    17     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    18     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    19 }
    20 const int N=2000+7;
    21 ll f[N][N],ans;
    22 int a[N],A[N],n,m;
    23 inline void dp(){
    24     for(register int i=1;i<=n;++i){
    25         ll minx=f[i-1][1];
    26         for(register int j=1;j<=m;++j)MIN(minx,f[i-1][j]),f[i][j]=minx+_abs(A[j]-a[i]);
    27     }
    28 }
    29 
    30 int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
    31     read(n);
    32     for(register int i=1;i<=n;++i)A[i]=read(a[i]);
    33     sort(A+1,A+n+1),m=unique(A+1,A+n+1)-A-1;
    34     dp();
    35     ans=f[n][1];
    36     for(register int i=2;i<=m;++i)MIN(ans,f[n][i]);
    37     reverse(a+1,a+n+1);
    38     dp();
    39     for(register int i=1;i<=m;++i)MIN(ans,f[n][i]);
    40     printf("%lld
    ",ans);
    41     return 0;
    42 }

    Upd2

    这题还有对应的拓展。(以及lyd付的思考题)

    • 把原序列改为不严格单调增序列,至少改多少数?
    • $ans=len-maxlen_{不下降子序列}$。原因很简单,保证有一个最长的不降序列,才能保证剩下的修改最少嘛。
    • 把原序列改为严格单调增序列,至少改多少数?
    • 令$B_i=A_i-i$。$ans=len-maxlen_{B的不下降子序列}$。这个比较重要。
    • 首先要考虑的问题是,严格单调递增→若原数列中两数相差超过下标差时,这段数一定要改一些数,否则改掉也“挤不下”。这是问题所在。
    • 要保证“挤得下”,就必须$A_j-A_i>=j-i $  $(i<j)$。即$A_j-j>=A_i-i$,才有严格递增。
    • 更准确地说,要$A_i<A_{i+1}Leftrightarrow A_i leqslant A_{i+1}-1Leftrightarrow A_i-ileqslant A_{i+1}-(i+1)Leftrightarrow B_ileqslant B_{i+1}$
    • 这是一个><号转≥≤的重要方法。所以要严格大于,我构造上述序列B,即可将其转化为大于等于。
    • 两者是等价的。
    • 方法是https://blog.csdn.net/guhaiteng/article/details/52549991的大佬的。tql%%%。

    然后这题就可以顺带切掉了。

    另外,将一开始问题的要求改为严格递增的最小代价,该怎么求?转化。同理,用上述方法构造序列。由于在原A序列和构造的B序列上修改代价是一样的,所以保证B满足不严格单调并且代价最小也就保证了A的严格单调。

    然后相关题目是CF714E。

    当然还有一个神仙的log做法。左偏树。我不会。逃。(要用log的做的题

  • 相关阅读:
    连接到 ASP.NET 数据库
    ASP.NET数据库操作代码小结:SQL Server篇
    无法远程连接 SQL Server 2008
    使用 ADO.NET 来创建、读取、更新和删除数据
    User表引发的"FROM子句语法错误"
    传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确
    ANSI,Unicode,UTF8网页编码的区别【转】
    ASP.NET 数据库原理【转】
    Eval()时间格式化
    Eclipse下无法自动编译,或者WEBINF/classes目录下没文件,编译失败的解决办法
  • 原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/10679688.html
Copyright © 2011-2022 走看看