动态规划(DP)入门

零、先修课程

首先,在开始理解DP的思想前,你需要

1. 完成HDU里面的递推求解专题练习(For Beginner)那7道题(这些题很简单,题解请在博客中搜索),这对你理解DP有很大的帮助。

2. 对递归搜索(比如深度优先搜索,DFS)有一定了解。

一、递归与记忆化搜索

我们从POJ 3176入手来学习这一思想。(题目很短,请快速读完)

从上往下看,最大和值无非是往左走和往右走这两条路的较大者。这样,我们可以写出下面这个重要的递归函数:(这里i表示行,j表示列)

int f(int i, int j)
{
    if (i == n - 1) return a[i][j];
    return a[i][j] + max(f(i + 1, j), f(i + 1, j + 1));
}

但是,当我们编完整个程序并通过了样例后,提交后却返回了一串鲜红的文字:

What happened?

我们仔细分析程序,发现当我们从(0,0)往下走时,路越走越多,和细胞分裂一样,最终的路大约有2^n条!

这实在是太吓人了,当我们回过神来,程序早已崩溃(爆栈)。

冷静点,孩子。让我们再仔细地看看所给的数据吧,嗯,一个三角形大小的数据,准确地说,是n(n+1)/2个数。

按常理来说,实际的路也只有约n^2条,怎么可能会产生指数级别的路径数呢?

没错,重复。

重复的计算造成了程序的最终崩溃,而减少重复的计算是程序优化的一个永恒的主题。

那么,重复在哪里,如何减少重复的计算?

再来看看题目所给的样例,我们把目光放在1这个点上:我们可以从7-3-1到达1,继续往下面算,也可以从7-8-1到达1,继续往下面算。因此,在上面的代码中,在1这个点我们算了两遍!同样的遭遇也发生在1下面的那些点中。大量的重复计算最终导致了指数条路径的产生。

所以,我们不妨在计算一个点之前,先看看它有没有已经被计算出来。

怎么看?嗯……也许我们需要一个辅助的数组来保存计算的结果,比如这样:

int f(int i, int j)
{
    if (dp[i][j] >= 0) return dp[i][j];
    if (i == n - 1) return dp[i][j] = a[i][j];
    return dp[i][j] = a[i][j] + max(f(i + 1, j), f(i + 1, j + 1));
}

由于a[i][j]可能为0,所以需要在main()中把dp初始化为-1:

memset(dp, -1, sizeof(dp));

最终,程序返回了我们想看到的结果:

 

此题的完整代码见这篇文章。

PS:关于打印路径的方法,见下面的第三部分。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索重复
, 递归
, dp
, 动态规划
, 搜索
, 程序
, return
, 路径规划怎么改文字
一个
dp动态规划、动态规划入门、数位dp入门、dp801单片机系统入门、树形dp入门,以便于您获取更多的相关知识。

时间: 2024-09-14 15:43:58

动态规划(DP)入门的相关文章

Acm一道动态规划的入门题,还请大神指点

问题描述 Acm一道动态规划的入门题,还请大神指点 描述 看,Alice和Bob又在玩游戏了. 游戏规则如下: Alice在纸上写出N个整数a,Bob写出一个整数S.他们轮流从这N个数中取任意个,使得这些数的和等于S.Alice先取.轮到某个人不能取时,这个人就输了.问最后的获胜者是谁.注意:两个人不能有相同取法. 输入 首先输入一个整数T,表示有T组测试数据(T≤100). 每组测试数据包含两行.第一行包含2个整数N(1≤N≤2000)和S(0≤S≤5000):第二行包含N个整数a(0<a≤1

算法:uva 1407 Caves (树形背包dp)

题意 一棵n个节点的树,树的边有正整数权,表示两个节点之间的距离.你的任务是回答这样的询问:从跟 节点出发,走不超过x单位的距离, 最多能经过多少节点?同一个节点经过多次, 只能算一个. 思路 这题同样是多天前看的, 在今天才想出解法的. 动态规划就是这么有意思 :) 遍历n个节点, 有两种情 况, 第一种是遍历完之后不回到出发点, 第二种是要回到出发点. 两种都可能会重复经过某些边, 但是显 然还是第二种遍历的花费会更大 在这一题中, 遍历之后不需要回到出发点. f(i, j, 0): 表示遍

uva 10688:The Poor Giant(区间dp)

题目链接: uva-10688 http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=514&page=show_problem&problem=1629 题意 有n个苹果,和一个数k,第i个苹果的重量是k+i(1<=i<=n). 已知其中只有一个苹果是甜的, 所有比它重量轻的都是苦的,比它重的都是酸的. 为了要找出甜的苹果,就要去一个一个地吃它,且吃了咬了苹果

算法:uva 10003 Cutting Sticks (区间dp)

题目大意 一根长为l的木棍,上面有n个"切点",每个点的位置为c[i] 要按照一定顺 序把每个点都砍段,最后变成了n+1段 每砍一次,就会有一个花费,例如长度为10,"切点"为2,那么砍完 后会变成两段2,8, 那么花费为2+8=10 如果有多个"切点",那么不同顺序切会得到不同的花费. 最小 花费是多少? 思路 注意要增加一个c[n] = l f(i, j) 表示(i,j)区间的最小花费 f(i, j) = min{ f(i,k)+f(k+1,

算法:zoj 3201 Tree of Tree(树形背包dp)

题意 给一棵节点带权的树,找到一个有k个节点的子树,求这个子树的最大权值 思路 树形 dp+背包. f(i, j) 表示以i为根节点的有j个节点子树的最大权值 然后对i的每个子节点做分组背包, 因为对于i的每个儿子,可以选择分配 1,2,3...j-1个节点给它 f(i, j) = max{ max{f(i, j-p) + f(v, p) | 1<=p<j} | v是i的儿子节点} ans = max{ f[i][k] | 0<=i<n && i 子树节点个数>

算法:uva 1351 String Compression(字符串区间dp)

题目大意 给一个字符串,可以把连续相同的部分进行缩写成k(S)的形式,S是一个字符串,k表示 有连续相同的S 例如,abgogogogo,可以缩写成ab4(go). 还可以嵌套缩写,比如 "nowletsgogogoletsgogogo", 缩写成"now2(lets3(go))" 思路 一道区间dp,但是这题并 不好想 f(i, j)表示字符串的i~j位的最小位数 那么 f(i, j) = min{  min{ f(i,k)+f(k+1, j), i<=k&

算法:ural 1018 Binary Apple Tree(树形dp | 经典)

题意 给一棵边有权值的二叉树,节点编号为1-n,1是根节点.求砍掉一些边,只保留q条边,这q条边 构成的子树 的根节点要求是1,问这颗子树的最大权值是多少? 思路 非常经典的一道树形dp题 ,根据我目前做过的题来看,有多道都是由这题衍生出来的. f(i, j) 表示子树i,保留j个节点(注意是 节点)的最大权值.每条边的权值,把它看作是连接的两个节点中的儿子节点的权值. 那么,就可以对所 有i的子树做分组背包,即每个子树可以选择1,2,...j-1条边分配给它. 状态转移为: f(i, j) =

算法:poj 4045 Power Station (树形dp)

题意 n个城市节点构成的一棵树,节点i到节点j的电量损耗为 I*I*R*(i到j的路径所含边数),现 在要在某个结点上修建一个供电站,使得这个结点到所有其它节点的总损耗量最小. 思路 典型的树形dp 可以先用一次dfs求出每一点的子树结点个数num[u],以及每一点到它子树所有结点的总 距离f[u][0]; 然后再用一次dfs,推出每个结点到除去它子树部分的结点总距离f[u][1]. f[v][1] = (f[u][0]-f[v][0]-num[v]) + f[u][1] + n - num[v

算法:poj 3345 Bribing FIPA (树形背包dp | 输入坑)

题意 有n个国家,你要获取m个国家的支持,获取第i个国家的支持就要给cost[i]的价钱 其中有 一些国家是老大和小弟的关系,也就是说,如果你获得了某个老大国家的支持, 那么这个国家的所有小弟 (包括小弟的小弟...递归下去)都会无偿免费支持你. 问最少的花费可以得到m个国家的支持 思路 这题还是比较好想的树形dp, 不过输入有些麻烦, 一开始以为每组样例结束都是'#',结果一直 RE,后来发现最后一组才是 '#'... 国家由于是直接给名字的,所以我用map<string, int>来映射保