【清北】【笔记】DP

3 篇文章 0 订阅

线性动规

拦截导弹:

n<1000的做法O(n²)
记f[i]为到第i个数的最长上升子序列的长度
比较简单,不再赘述

n<100000 nlogn做法:
g[x]以不大于x为结尾的最长不上升子序列的长度
单调不减
f[i]max=1,
g[a[i]-1]+1
求值过程需要树状数组实现

DP:一般来说n²可拿60分QAQ

数字三角形:

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

数字三角形+
f[i][j][k]为1或0,判断余数是否可能出现
防止取模变成负的+233

数字三角形++
n<10000
O(n²)
每一段路径被限制在一个梯形里
状态变稀疏
d数组为必经点,从上往下排好序
先判不可能出现的情况
滚动数组优化(卡空间):由于只涉及上一行信息,那么就只保存上一行,之前的扔掉。。g[i]只存当前行的dp信息。

拦截导弹+
枚举
去重方法:从后往前扫一遍,只记录靠后的g[i]


区间动规

多边形表达式
n<200
O(n³)如果枚举断边就会->n^4
倍长一遍(长度为N->2N),枚举只要枚举长度为n的答案就可以了->O(n³)
长度可以递推
a[] 偶数下标:符号 奇数下标:数组(可以是负数)
预处理自己到自己的情况
f[i][j]表示从 i到j的表达式加括号后可达的最大值->变成子问题
ps:由于含有乘法和减法以及负数,所以只记f是不够的
g[i][j]表示……最小值

f[i][j]=枚举k max {
                    f[i][k-1] opk f[k+1][j]           + *
                    f[i][k-1] opk g[k+1][j]           -
                    g[i][k-1] opk f[k+1][j]
                    g[i][k-1] opk g[k+1][j]            *
                            }
g[i][j]=min{
                   ……
                   }

opk:一个计算的函数
过程:枚举长度
区间动规的转移是以长度来转移的
也可以 f[L][i]:以i开始长度为L的最大值

取数问题
状态:时间、序列
Ti是数组

f[i][j][k]表示时刻i队列为j---k的最大和
f[i][j][k]={
               f[i-1][j][k-1]+a[k]*t[i]
               f[i-1][j+1][k]+a[j]*t[i]
              }
ans=f[i][j][k-1]

O(n³) n<1000—–>考虑优化

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

代码:枚举长度来做

石子合并
贪心,小根堆优化
每次取出最小两堆合并,再把合并后的堆扔进去

石子合并+
n<60
O(n³)

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

石子合并++
n<10000,k<100
脑筋急转弯??????????
一次合并就好了QwQ

石子合并+++
贪心题目
补零操作:补到每次合k堆可以合完
k叉哈弗曼树
哈弗曼编码:字符串都有出现次数,编排序号,出现次数最多的放在前面
每次选择最小的k堆合在一起

删数游戏
要求删除两个数必须相邻
n<60 A<100000
只考虑n为偶数的情况(n为奇数肯定不可以)
区间 :i—-j
f[i][j]表示I—j这个区间能否被删完(0/1)状态
k、l为假定最后删的两个数的下标

f[i][i]=0
f[i][i-1]=1//空区间(代码方便)

f[i][j] | =f[i][k-1]&&
             f[k+1][l-1]&&
             f[l+1][j]&&
             (a[l]-a[k] in A)
//|:只要有一个可以删掉即可
i<=k<l<=j
Ans=f[1][n]
O(n^4)

枚举区间长度l

f[i][j]|=f[i+1][k-1]&f[k+1][j]&(a[k]-a[i] in A)
         f[i+1][j-1]&(a[j]-a[i] in A)
O(n³)

删数游戏+


f[i][j]|=f[i+1][k-1]&f[k+1][j]&(a[k]-a[i] in A)
         i<k<=j,最后删a[i],a[k]//删两个

f[i][j]|=f[i+1][k-1]&f[k+1][l-1]&f[l+1][j](a[l]-a[k] = a[k]-a[i])
         &(a[k]-a[i] in A)//删三个
         i<k<l<=j 最后删a[i],a[k],a[l]

记忆化搜索

滑雪
无后效性保证:不能往回滑
f[i][j]表示到(i,j)的最长单调减序列长度

f[i][j]max={
                 f[i-1][j]+1,a[i-1][j]>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][j-1]+1,a[i][j-1]>a[i][j]
                 }

背包动规

背包问题
暴力

背包问题+
f[i][t]表示到第i个物品,背包剩余容量为t的最大价值
可以使用滚动数组g[i]记录上一轮情况

背包问题++
分成两半A、B分别暴力枚举方案
M:背包体积
A:暴力枚举的方案 C:方案的价值
B:暴力枚举的方案 D:方案的价值
A由小到大排序,B由大到小排序
记录A的前缀最大价值,记录B的后缀最大价值
由于体积很大:
d[i][t]表示到第i个物品,总价值为t的最小容量
f[i][t]=min(f[i-1][t],f[i-1][t-c[i]]+w[i])
ans=max t,f[n][t]<=m

背包问题+++
减少冗余的枚举:二进制拆分
01背包问题
or 单调队列解法:没听懂http://blog.csdn.net/flyinghearts/article/details/5898183


树形动规

最长链
贪心/暴力做法:DFS/BFS
DP做法:复杂度是线性的
枚举折点,自底向上DP
记d[i]表示以i为根节点的向下的最长链

ans[x]=2+max d[si]+d[sj]  特判只有一个叶节点:ans[x]=1+d[s1]
d[x]=1+max d[y]               y是x的子节点
ans[x]表示以x为根的子数中,经过x的最长链
ans[x]max=2+d[y1]+d[y2]
                   1+d[y]         

最长链+
贪心/暴力:DFS
DP做法:

 d[x]=max d[y]+e[x][y]
 ans[x]max=e[x][y1]+d[y1]+e[x][y2]+d[y2]
                    e[x][y]+d[y]

最长链++
DP做法还是可以的
DFS不能处理边权是负值的图

最大点权独立集
没有上司的舞会???
f[i][0] 表示以i为根的子树没选i的最大权值和
f[i][1] 表示以i为根的子树选了i的最大权值和
j是i的子节点

f[i][0]=sigma max f[i][1],f[i][0] (子节点随意)
f[i][1]=(sigma f[j][0])+v[i]

最大点权独立集+
首先如何判环?DFS到自己就找到了环。
处理环套树问题:算法复杂度是线性的
第一种做法:
枚举情况,把环断开
第二种做法:
把树收缩成一个点,在环上处理。环上取或不取都带上了权值,求在换上的最大点权独立集,随便枚举环上的点。

树上背包

给定一棵树,每个节点上有一个物品。
选择每个物品的前提是该物品到根节点上的物品都已经被选
过了。
每个物品有体积和价值,给定一个容量为m 的背包,求能拿
到多少价值。
n<1000,m<100

从下往上推,一个点的信息只能由子节点更新而来 O(NM²)

f[i][j]更新到i节点时已用至多j容量背包时的价值
f[i][0]=0 j<wi 一个都取不到
(j>0 背包放了东西) 
j>=wi 
f[i][j]至少有一个i 假设k1,k2 为他的左右节点
f[i][j]=c[i] + f[k1][t] + f[k2][j-t-w[i]] 
0<=t<=j-w[i]
f[i][j]=c[i]+f[k1][j-wi]
f[i][j]=c[i] j>=w[i](天然满足
f[i][j]=max f[i][t] t<=j

树上背包+
O(NM²)
多叉转二叉:s个子节点,s-2个新建节点(毛毛虫转法????QAQ


状压DP

简单题
用int写
f[i][mask]到第i行1–i选取状态为mask时方案数
f[i][mask]=f[i-1][mask] 没取
+f[i-1][mask’]

简单题+
每一行只影响下一行
反推
边界:f[0][mask]=1
f[i][mask]到第i行的取法为mask的方案数
枚举mask’(与mask不冲突
f[i][mask]=sigma f[i-1][mask’]
ans=sigma f[i][mask] mask:0~2^m -1
判断可行方案:
mask’&~mask==mask’&&//mask’在mask里
valid[mask’]//mask’没有相邻的1
找mask’:
枚举~mask所有子集
令c=~mask
while(c>0){
c&~mask–>mask’
c=(c-1)&~mask’//枚举下一个
}
栗子(枚举串的子串):O(3^m)
~mask=010110
c: 010110 &—>010110
-1= 010101 &—>010100
-1= 010011 &—>010010
-1= 010001 &—>010000
-1= 001111 &—>000110
-1= 000101 &—>000100
-1= 000011 &—>000010
-1= 000001 &—>000000
if(mask&(1<< i)&&mask&(1<<(i-1)))
valid[mask]=0;
PS:也可以 mask>>1&mask ==0
奇数和偶数&了一下=0 即合法

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值