zoukankan      html  css  js  c++  java
  • 国庆七天乐——第三天

    2017102

    【【动态规划】】

    【基础内容】

    状态设计:往往是观察在搜索过程中需要用到的参数,所表

    示的含义往往是“最大”、“最小”、“方案总数”、“0/1”。

    状态转移方程一般以数列递推的形式给出,在研究如何代码实现

    转移设计:状态都有什么,应该如何转移,

    注意无后效性

    【线性动态规划】(所有你不能分到其它类里的dp就叫线性动态规划)

       1拦截导弹

           f[i]表示到第i个数的最长上升子序列的长度

       f[i]=1

          f[j]+1   j<I,a[j]<a[i]

       滚动数组优化:当前状态只与上一层(或几层)有关,就新开一个数组g[n],表示上一层的状态

       g[x]表示以不大于x结尾的最长上升子序列,单调不减

           f[i]=1

          g[a[i]-1]+1   j<I,a[j]<a[i]

                                                                                        #n^2#

      优化:用树状数组来优化ànlogn

    2拦截导弹+

      求方案数。

    开一个g[i]数组来表示当前这个数是由之前的几个数转过来的

         g[i]=sigma(g[j]) ,  j<I,  f[j]=f[i]-1

                            a[j]>a[i]

    去重1:g只加最近的满足条件的g[j]

    去重2:在整个数列的末尾(++n)加一个INF/max

            这样在找最长上升子序列时,长度就直接是f[n],最后的答案就是g[n].(更加优美)

    3数字三角形

    f[i][j]=max(f[i-1][j],f[i-1][j-1])+a[i][j]

          a是前缀和

    4.数字三角形+(所得和数对233取模)

           f[i][j][k]=f[i-1][j][(k-a[i][j]+233)%233]

                  | f[i-1][j-1][(k-a[i][j]+233)%233]

           因为模数很小,所以可以用同余类来优化,

       把余数相同的数分为一类,再加一维

       因为c++负数取模还是负数,所以要再加233来防止出现素数

    5.数字三角形++(有必须经过的点)

                            红点为必须经过的点

    蓝色方框为可能经过路线的范围

    这样我们dp的范围就限制在了每个方块内了,减少了计算量 

    【区间动态规划】

    1. 多边形表达式(环)

    先化简:已知表达式,求加括号后的最大值

       f[i][j]表示从i到j的表达式加括号后可达到的最大值

       g[i][j]表示从i到j的表达式加括号后可达到的最小值

       f[i][j]= 枚举k   f[i][k-1] opk f[k+1][j]   +  *

                     f[i][k-1] opk g[k+1][j]   -

                                         g[i][k-1] opk k[k+1][j]   *

                                         g[i][k-1] opk g[k+1][j]   *

       分别来表示每种运算的最优结果

    回归原题,多边形表达式,就是把一个环删掉一条边,变成一条链,然后依次枚举断开的边,但显然这样做是超时的,所以我们把这个链乘2,在去枚举每一个起点(长度为n),这样就可以依次遍历了(破环成链经典做法)

    1. 取数问题

    F[i][j][k]表示时刻i,队列变为j-k的最大值

       Ans=f[n][j][j-1];

    f[i][j][k]  max  =f[i-1][j][k-1]+a[k]*t[i]

                   f[i-1][j+1][k]+a[j]*t[i]

    当前这个时刻,取左右端点的最大值,当前这个时刻,由上一个时刻推来。如果是优化的话,可以把i这一维删掉。因为i=n-(k-j+1),所以用i的地方都可以用n-(k-j+1)代替(ti=总长-当前长)

    f[i][j][k]  max  =f [j][k-1]+a[k]*t[n-(k-j+1)]

                   f[j+1][k]+a[j]*t[n-(k-j+1)]

    1. 合并果子 (堆)à哈夫曼编码

    找到一个编码(无歧义)方式,试每个字符串的长度*出现次数的和最小。

    实现方法就是维护一个每次合并前k小的优先队列

    1. 合并石子(相邻)

    f[i][j]=f[i][k]+f[k+1][j]+s[i]+s[j];

    1. 合并石子+ 任意合并相邻2到n堆,求最少能量的消耗

    脑筋急转弯解法:直接全部合并就好了。

    1. 合并石子++ 每次任意合并至少k堆,求最少能量的消耗

    依旧是维护一个优先队列,但是要在队列末尾补0,来保证一定能实现k堆的合并且不影响答案。(小技巧)

    1. 删数游戏

      f[i][j]表示i-j能否被删完(bool)

      f[i][i]=0,f[i][i-1]=1

             表示一个空区间,空区间是删除成功的,所以为1,同时也是为了下面打码方便。

      f[i][j] |=(f[i][k-1]&f[k+1][l-1]&f[l+1][j])&a[l]-a[k]∈A)

      左边被删完了,中间被删完了,右边被删完了,而且它们的等差在A里面。

    优化:因为一个ak不可能出现在两个x中间这种情况(这两个x满足被删掉的理由),因为一旦出现了,那就说明ak应该早就被删掉了,所以只有可能是左边删完,右边删完。

    f[i][j] |=f[i-1][k-1]&f[k+1][j] &a[l]-a[k]∈A) i<k<j

              f[i+1][j-1]&(a[j]-a[i] ∈A) k==j

    /************** |= 或等于 ****************************/

    在所有的情况中,只要有一种是满足的就是1,否则为0

    /*****************************************************/

    8.删数游戏+  删去2到3个长度的连续等差数列

    分类讨论

    两个长度的同上,三个长度的如下

    f[i][j]|=f[i][l]&f[l+1][j]&a[l]+a[i]=2*a[k]&a[k]-a[i]in A

    之前是枚举它的k的位置,怎么删,然而我们并不关心这个,我们只需要知道它能不能删就好了,所以依然是左右两段。

    【记忆化搜索】

    记忆化搜索其实是一种DP 求解的方法,而非特定的问题。

    该方法一般用于较难确定更新顺序的DP 问题。

    1. 滑雪

    f[i][j] 表示到(i,j)的单调递减序列长度

    f[i][j] max = f[i][j+1]+1,  a[i][j+1]>a[i][j]

                  f[i+1][j]+1,  a[i+1][j]>a[i][j]

                  f[i][j-1]+1,  a[i][j-1]>a[i][j]

                  f[i-1][j]+1,  a[i-1][j]>a[i][j]

      滑雪的从上到下且不会回去的性质确定了这个题的无后效性,所以这个题可以用dp做。

      枚举四个方向取一个max,同时不要忘了确定边界。

    /********记忆化的精髓**************************/

    if(vis[i][j]) return vis[i][j]

    2. 数字三角形++

    If(x1==x2&&y2!=y1) return –inf;

    本来在起点的两个坐标的y值不相等

    If(vis[x2][y2]) return vis[x2][y2];

    If(y2<y1) return –inf;

    往左下走,不可能

    If(x2<=x1) return –inf;

    向上走,不可能

    【背包问题】

    背包动规是一类经典的装箱问题的解决方案,也是将数值放

    入DP 方程的典型代表。

    1.0/1背包

    For(i:1-n)

      For(j:m-1)

    f[j]=max(f[j],f[j-w[i]]+c[i];

    2.完全背包

    For(i:1-n)

      For(j:1-m)

    f[j]=max(f[j],f[j-w[i]]+c[i];

    3. 多重背包

    二进制优化,

    单调队列优化,剩余类分类à滑动窗口à单调队列。

    f[i][t] max =f[i-1][++j*w[i]]+j*c[i]

           0<=j<=k[i],j*w[i]+t<=m

    Step1:

      f[i][r+p*w[i]]*w[i]] max=g[i-1][r+(i+p)*w[i]]

         0<=j<=k[i], (j+k)*w[i]+r<=m

    Step2:

       f[i][r+p*w[i]]-=p*c[i];

    #m*sigma(ki)#

    4. 厉害一点的暴力背包

    题面:一个容量为m 的背包,有n 个物品,第i 个物品的体积为wi,价值为ci。选择若干物品,使得体积总和不超过m 的情况下价值总和最大。

    n<40,m 可能很大Meet in the middle!

    解法,讲n分成两部分,

       A 表示在前半部分中数据从小到大

       B 前半部分的价值

       C 表示在后半部分中数据从大到小

       D 后半部分的价值

     在A中枚举断点,因为An+Cn<=M,所以可以确定Cn,(都是一个范围)然后分别在其中找到所有里的最大的价值相加,就是当前这个断点的最优值,然后在所有最优值中找出最值,就是结果

    5. 圆滑数

    乘积末尾为0的数只有两种,

    有一个(或多个)数本身为0

    2*5;

    所以我们就先求出2,和5在每个数里(乘积)出现的次数,(n2,n5)然后就是一个背包问题来计算所有数中2和5的数(试从中选k个)

    f[i][k][j]=max(f[i-1][k][j],f[i-1][k-1][j-n5[i]]+n2[i])

    ans=max(ans,min(j,f[n][k][j])

    如果数据中有0的话,要加特判,保证不能为0

    If(a[i]==0)

    {
    ans=1,vis[i]=1;continue;

    //用vis来做标记}

    背包的判断也要改成

    If(j>=n5[i]&&!vis[i])

  • 相关阅读:
    APPlication,Session和Cookie的区别
    C# 中的Request对象的应用
    从字符串里提取一个列表(数组)
    UDP:用户数据报协议
    反射
    网络编程
    多线程
    final,finally和finalize的区别
    集合
    StringBuffer
  • 原文地址:https://www.cnblogs.com/ZDHYXZ/p/7628706.html
Copyright © 2011-2022 走看看