国庆七天乐——第三天

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])

原文地址:https://www.cnblogs.com/ZDHYXZ/p/7628706.html