菜单
菜单
文章目录
  1. 1.抢金币
  2. 2.节点选择
  3. *3.解码
  4. 4.最长连续数列
  5. 5.最大交易利润
  6. 6.最长增序列
  7. 7.机器人移动
  8. 8.最大整除集
  9. 9.字符串分割
  10. 10.蛙跳
  11. 11.最小路径和
  12. 12.子序列
  13. 13.爬楼梯
  14. 14.同义子集
  15. 15.回文
  16. - 16.递推:
  17. 17.背包
  18. 18.LIS
  19. 19.LCS
  20. 20.树形dp
  21. 21.数位dp
  22. 22.概率(期望) dp
  23. 23.状态压缩dp
  24. 24.二进制优化
  25. 25.单调队列优化
  26. 26.斜率优化
  27. 27.四边形不等式优化

算法(二):动态规划

1.抢金币


dp[n]={w[1]n=1max(dp[1],dp[2])n=2max(dp[n1],dp[n2]+w[n])n3dp[n]=\begin{cases} w[1] & n=1 \\ max(dp[1],dp[2]) & n=2 \\ max(dp[n-1],dp[n-2]+w[n]) & n\geq3 \end{cases}

第二问随机选一个标号为1

选1就不能选2和n,剩余n-3个房子用第一问求

不选1就等于求等于剩余n-1个房子用第一问求

max(w[1]+dp[n3],dp[n1])max(w[1]+dp[n-3],dp[n-1])

2.节点选择

如果选择root,最大值=四个孙子树的最大值之和

如果不选择root,最大值=两个子树的最大值之和

dp[n]={noden.vif node is leafmax(noden.v+dp[ngrandson],dp[nson])if node is not leafdp[n]=\begin{cases} node_n.v & if \ node \ is \ leaf\\ max(node_n.v+\sum dp[n_{grandson}],\sum dp[n_{son}]) & if \ node \ is \ not \ leaf \end{cases}

*3.解码

dp[i]={1if i=1,00if Si=0 and Si11 or 2dp[i1]+dp[i2]if 10<(Si110+Si)26 and (Si110+Si)20dp[i2]if (Si110+Si)=10 or 20dp[i1]othersdp[i]=\begin{cases} 1 & if \ i = 1,0 \\ 0 &if \ S_i=0\ and \ S_{i-1} \ne 1 \ or \ 2\\ dp[i-1]+dp[i-2] & if \ 10<(S_{i-1}*10+S_i)\leq26 \ and \ (S_{i-1}*10+S_i)\neq20\\ dp[i-2] &if \ (S_{i-1}*10+S_i)=10\ or \ 20 \\ dp[i-1] & others \end{cases}

这道题最终可以转化成斐波那契数列进行计算。

首先要确定的是,只有在数值小于26的时候,才可能出现多种解码。

  • 例如,当数字是12,可以解码成AB或者L,当数值大于26则只有唯一解码
  • 例如,当数字是34,解码只有CD

因此可从这个角度出发来解决这个问题,即先对数字进行判断

  • 如果大于2,则进行切分
  • 如果等于二,考虑后面一个数是否大于6
    • 如果大于6,则切分
    • 不大于6则不变
    • 例如:
      • 12213411可以切分成12213、4、11三部分
      • 1227126可以切分为122、7、126三部分
  • 之后对拆分之后的每个子集进行分析,分析的主要依据和之前类似,由于无论如何组合,能够进行解码的数字一定不能是三位数,所以对每一个子集再进行组合,这时候你会发现,在上面讨论的前提下,会形成一个解码个数随子集中元素个数变化的斐波那契数列(这个斐波那契数列没有第一项的1),求得每个子集解码个数之后,将这些数字相乘就得到了最终结果。
a = input()
b = []
id_head = 0
for i in range(len(a)):
if int(a[i])>2:
b.append(a[id_head:i+1])
id_head = i+1
elif i == len(a)-1:
b.append(a[id_head:i + 1])

num = 1
fib = lambda n:1 if n<=2 else fib(n-1)+fib(n-2)

for i in range(len(b)):
num = num * fib(len(b[i])+1)
print(num)

4.最长连续数列

OPT[i]=max{opt[i1]if L[i] not in longestifirst[r],r=w=0iL[w]modkif L[i] in longestOPT[i]=max\begin{cases} opt[i-1] & if \ L[i] \ not \ in \ longest \\ i-first[r],r = \sum_{w=0}^{i}L[w] \mod k & if \ L[i] \ in \ longest \end{cases}

j=first[r]是指第一次,即 argminw=0jL[w]=rarg \min\sum_{w=0}^{j}L[w]=r

5.最大交易利润

6.最长增序列

opt[i]表示如果找长度为i的单增数列,最小的结尾是opt[i]

nums表示输入的数组

如果nums[j]>opt[i]那么就让opt[i+1]=nums[j]

如果nums[j]<opt[i-1],就去比opt[j-1]、opt[i-2],

​ 如果opt[1~i-1]都大于nums[j],更新opt[1]=nums[j]

​ 如果存在opt[m]<nums[j]<opt[n],利用二分法去找n,更新opt[n]=nums[j]

最终len(opt)就是最大长度

opt[i]={nums[1]if i=1nums[i]if opt[i1]<nums[i]nums[j]if opt[i1]<nums[j]<opt[i]opt[i] = \begin{cases} nums[1] &if \ i=1\\ nums[i] &if \ opt[i-1]<nums[i]\\ nums[j] &if \ opt[i-1]<nums[j]<opt[i] \end{cases}

7.机器人移动

令opt[i,j]表示在(i,j)的所有解

opt[i,j]={topk[s[1,1]]if i,j=1,1topk[opt[1,1].join(s[1,2])]if i,j=1,2topk[opt[1,1].join(s[2,1])]if i,j=2,1topk[opt[i1,j].join(s[i,j])opt[i,j1].join(s[i,j])]otheropt[i,j]=\begin{cases} top_k[s[1,1]] & if \ i,j=1,1\\ top_k[opt[1,1].join(s[1,2])] & if \ i,j=1,2\\ top_k[opt[1,1].join( s[2,1])]& if \ i,j=2,1\\ top_k[opt[i-1,j].join( s[i,j]) \cup opt[i,j-1].join(s[i,j])] & other \end{cases}

2017年习题

8.最大整除集

将数组从小到大排列,记opt[i]为输入数列遍历到i时最大的整除集长度

opt[i]={max(opt[j]+1)if s[i]%s[j]=01otheropt[i] = \begin{cases} max(opt[j]+1) & if \ s[i]\% s[j]=0\\ 1 & other \end{cases}

9.字符串分割

dp[i]={0if s[i:]is palindromemin(dp[j])+1for j=i+1n and s[i:j]is palindromedp[i]=\begin{cases} 0 & if \ s[i:] is \ palindrome \\ min(dp[j])+1 & for \ j=i+1 \to n \ and \ s[i:j] is\ palindrome \end{cases}

定义状态数组:cut[s.length()+1],其中:cut[i]代表:string[i…n]字符串从i开始到末尾的最小划分数。
状态转移方程: cut[i] = min(cut[i], cut[j+1]+1); i+1<=j<=n-1
状态转移方程的意思是,string[i…j]是一个回文字符串,所以不用再划分。所以从i开始到末尾以j为划分点的最小划分数为: cut_num_array[j+1]+1 和 cut_num_array[i]中的最小值。
cut_num_array[i]的初值设为:s.length() - i; 也就是按照字符串中的每个字母都单独被划分来计算。

2016年习题

10.蛙跳

青蛙过河,上一次跳k长度,下一次只能跳k­-1,k或者k+1。 因此对于到达了某一个点,我们可以查看其上一次是从哪个点跳过来的。

设dp[ j ][ i ] 为从i到达j 的步数,初始时把所有的石头存放进hash表。然后设置dp[0][0] = 0. 接着对于每个石头,从 可以到达该石头的所有石头中取出步数k(k > 0),然后当前的stone + k看其是否是合法的石头,是的话就有 d[stone + k ][stone] = k

def can_cross(stones):
dp = {stone: {} for stone in stones}
dp[0][0] = 0
for stone in stones:
for step in dp[stone].values():
for k in [step + 1, step, step ‐ 1]:
if k > 0 and stone + k in dp:
dp[stone + k][stone] = k
return len(dp[stones[‐1]].keys()) > 0

优化

def canCross(self, stones):
"""
:type stones: List[int]
:rtype: bool
"""
stone_set, fail = set(stones), set()
stack = [(0, 0)]
while stack:
stone, jump = stack.pop()
for j in (jump-1, jump, jump+1):
s = stone + j
if j > 0 and s in stone_set and (s, j) not in fail:
if s == stones[-1]:
return True
stack.append((s, j))
fail.add((stone, jump))
return False

2015年习题

11.最小路径和

贪心加递归:

每一次都是往左下或者右下

opt[Noden]=min{opt[nodePl],opt[nodepr]}+Noden.vopt[Node_n]=min\{opt[node_{P_l}],opt[node_{p_r}]\}+Node_n.v

12.子序列

dp[i][j]= dp[i-1][j-1] \and S[i]==T[j]

dp[i][j]表示S[0:i]是否为T[0:j]的子序列

2017级考题

13.爬楼梯

dp[i]={iif i3dp[i1]+dp[i12]otherdp[i]= \begin{cases} i & if\ i\leq3 \\ dp[i-1]+dp[i-1-2] & other \end{cases}

2016级考题

14.同义子集

每次都需要比i-1次,复杂度为O(n2)O(n^2)

令dp[i]表示,有i个相同句子的集合

利用并查集这一数据结构,如果存在si=sjs_i=s_j(i<j),那么就让sjs_j中移走,并让dp[si]=dp[si]+1dp[s_i]=dp[s_i]+1

如果sjs_j和S中的句子都不相等,那么就让dp[sj]=1dp[s_j]=1,然后继续遍历下一个

15.回文

补充题

一、简单基础dp

这类dp主要是一些状态比较容易表示,转移方程比较好想,问题比较基本常见的。主要包括递推、背包、LIS(最长递增序列),LCS(最长公共子序列),下面针对这几种类型,推荐一下比较好的学习资料和题目。

- 16.递推:

递推一般形式比较单一,从前往后,分类枚举就行。

简单:

hdu 2084 数塔 简单从上往下递推

hdu 2018 母牛的故事 简单递推计数

hdu 2044 一只小蜜蜂… 简单递推计数(Fibonacci)

hdu 2041 超级楼梯 Fibonacci

hdu 2050 折线分割平面 找递推公式

推荐:

CF 429B B.Working out 四个角递推

zoj 3747 Attack on Titans 带限制条件的计数递推dp

uva 10328 Coin Toss 同上题

hdu 4747 Mex

hdu 4489 The King’s Ups and Downs

hdu 4054 Number String

17.背包

经典的背包九讲:http://love-oriented.com/pack/

推荐博客:http://blog.csdn.net/woshi250hua/article/details/7636866

主要有0-1背包、完全背包、分组背包、多重背包。

简单:

hdu 2955 Robberies 01背包

hdu 1864 最大报销额 01背包

hdu 2602 Bone Collector 01背包

hdu 2844 Coins 多重背包

hdu 2159 FATE 完全背包

推荐:

woj 1537 A Stone-I 转化成背包

woj 1538 B Stone-II 转化成背包

poj 1170 Shopping Offers 状压+背包

zoj 3769 Diablo III 带限制条件的背包

zoj 3638 Fruit Ninja 背包的转化成组合数学

hdu 3092 Least common multiple 转化成完全背包问题

poj 1015 Jury Compromise 扩大区间+输出路径

poj 1112 Team Them UP 图论+背包

18.LIS

最长递增子序列,朴素的是o(n^2)算法,二分下可以写成o(nlgn):维护一个当前最优的递增序列——找到恰好大于它更新

简单:

hdu 1003 Max Sum

hdu 1087 Super Jumping!

推荐:

uva 10635 Prince and Princess LCS转化成LIS

hdu 4352 XHXJ’s LIS 数位dp+LIS思想

srm div2 1000 状态压缩+LIS

poj 1239 Increasing Sequence 两次dp

19.LCS

最长公共子序列,通常o(n^2)的算法

hdu 1503 Advanced Fruits

hdu 1159 Common Subsequence

uva 111 History Grading 要先排个序

poj 1080 Human Gene Functions

二、区间dp

推荐博客:http://blog.csdn.net/woshi250hua/article/details/7969225

区间dp,一般是枚举区间,把区间分成左右两部分,然后求出左右区间再合并。

poj 1141 Brackets Sequence 括号匹配并输出方案

hdu 4745 Two Rabbits 转化成求回文串

zoj 3541 The Last Puzzle 贪心+区间dp

poj 2955 Brackets

hdu 4283 You Are the One 常见写法

hdu 2476 String Printer

zoj 3537 Cake

CF 149D Coloring Brackets

zoj 3469 Food Delivery

20.树形dp

比较好的博客:http://blog.csdn.net/woshi250hua/article/details/7644959

一篇论文:http://doc.baidu.com/view/f3b19d0b79563c1ec5da710e.html

树形dp是建立在树这种数据结构上的dp,一般状态比较好想,通过dfs维护从根到叶子或从叶子到根的状态转移。

hdu 4123 Bob’s Race 二分+树形dp+单调队列

hdu 4514 求树的直径

poj 1655 Balancing Act

hdu 4714 Tree2Cycle 思维

hdu 4616 Game

hdu 4126 Genghis Kehan the Conqueror MST+树形dp 比较经典

hdu 4756 Install Air Conditioning MST+树形dp 同上

hdu 3660 Alice and Bob’s Trip 有点像对抗搜索

CF 337D Book of Evil 树直径的思想 思维

hdu 2196 Computer 搜两遍

21.数位dp

推荐一篇论文:http://wenku.baidu.com/view/d2414ffe04a1b0717fd5dda8.html

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快。数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来。

hdu 2089 不要62 简单数位dp

hdu 3709 Balanced Number 比较简单

CF 401D Roman and Numbers 状压+数位dp

hdu 4398 X mod f(x) 把模数加进状态里面

hdu 4734 F(x) 简单数位dp

hdu 3693 Math teacher’s homework 思维变换的数位dp

hdu 4352 XHXJ’s LIS 数位dp+LIS思想

CF 55D Beautiful Numbers 比较巧妙的数位dp

hdu 3565 Bi-peak Numbers 比较难想

CF 258B Little Elephant and Elections 数位dp+组合数学+逆元

22.概率(期望) dp

推荐博客:http://www.cnblogs.com/kuangbin/archive/2012/10/02/2710606.html

推荐博客:http://blog.csdn.net/woshi250hua/article/details/7912049

推荐论文:

《走进概率的世界》

《浅析竞赛中一类数学期望问题的解决方法》

《有关概率和期望问题的研究》

一般来说概率正着推,期望逆着推。有环的一般要用到高斯消元解方程。期望可以分解成多个子期望的加权和,权为子期望发生的概率,即 E(aA+bB+…) = aE(A) + bE(B) +…

ural 1776 Anniversiry Firework 比较基础

hdu 4418 Time travel 比较经典BFS+概率dp+高斯消元

hdu 4586 Play the Dice 推公式比较水

hdu 4487 Maximum Random Walk

jobdu 1546 迷宫问题 高斯消元+概率dp+BFS预处理

hdu 3853 LOOPS 简单概率dp

hdu 4405 Aeroplane chess 简单概率dp,比较直接

hdu 4089 Activation 比较经典

poj 2096 Collecting Bugs 题目比较难读懂

zoj 3640 Help me Escape 从后往前,比较简单

hdu 4034 Maze 经典好题,借助树的概率dp

hdu 4336 Card Collector 状态压缩+概率dp

hdu 4326 Game 这个题状态有点难抽象

23.状态压缩dp

这类问题有TSP插头dp等。

推荐论文:http://wenku.baidu.com/view/ce445e4f767f5acfa1c7cd51.html

推荐博客:http://blog.csdn.net/sf____/article/details/15026397

推荐博客:http://www.notonlysuccess.com/index.php/plug_dp/

hdu 1693 Eat the Trees 插头dp

hdu 4568 Hunter 最短路+TSP

hdu 4539 插头dp

hdu 4529 状压dp

poj 1185 炮兵阵地

poj 2411 Mandriann’s Dream 轮廓线dp

hdu 3811 Permutation

poj 1038

poj 2441

hdu 2167

hdu 4026

hdu 4281

七、数据结构优化的dp

有时尽管状态找好了,转移方程的想好了,但时间复杂度比较大,需要用数据结构进行优化。常见的优化有二进制优化、单调队列优化、斜率优化、四边形不等式优化等。

24.二进制优化

主要是优化背包问题,背包九讲里面有介绍,比较简单,这里只附上几道题目。

hdu 1059 Diving

hdu 1171 Big Event in Hdu

poj 1048 Follow My Magic

25.单调队列优化

推荐论文:http://wenku.baidu.com/view/4d23b4d128ea81c758f578ae.html

推荐博客:http://www.cnblogs.com/neverforget/archive/2011/10/13/ll.html

hdu 3401 Trade

poj 3245 Sequece Partitioning 二分+单调队列优化

26.斜率优化

推荐论文:用单调性优化动态规划

推荐博客:http://www.cnblogs.com/ronaflx/archive/2011/02/05/1949278.html

hdu 3507 Print Article

poj 1260 Pearls

hdu 2829 Lawrence

hdu 2993 Max Average Problem

27.四边形不等式优化

推荐博客:http://www.cnblogs.com/ronaflx/archive/2011/03/30/1999764.html

推荐博客:http://www.cnblogs.com/zxndgv/archive/2011/08/02/2125242.html

hdu 2952 Counting Sheep

poj 1160 Post Office

hdu 3480 Division

hdu 3516 Tree Construction

hdu 2829 Lawrence