dp类:
A - Bridging signals ZOJ 3627 POJ1631 HDU1950
给出一个从1-n的数字排列,求最长上升子序列长度。直接说解法吧。新开一个数组d,d[i]表示的是能构成长度为i的上升子序列的在原序列中最后的那个数值。程序的主要过程:当循环到第i个的时候,如果原序列中的第i个数值大于之前d中保存的上升序列中长度最长的那个最后的值,那么,就把当前记录的最长的子序列的长度+1,然后把这个值加到d的末尾;如果不大于,那么就从前面二分找到这个值,d中的序列一定是有序的,找到d总刚刚大于它的那个值,替换掉。
C++中可以利用lower_bound来实现二分查找
B - ZOJ1733 POJ1458 Common Subsequence,经典DP问题
http://wenku.baidu.com/view/1b4c4eb069dc5022aaea00b3.html
Palindrome POj 1159 http://www.cnblogs.com/yueshuqiao/archive/2011/08/27/2155738.html
C - Human Gene Functions POJ 1080
跟前面两个基本一样。只不过多了权值。注意初始化。思想是一样的。
E - 湫湫系列故事——减肥记I 背包问题 POJ 4508
完全背包,详见背包九讲
F - Multiplication Puzzle POJ 1651 矩阵乘法
给出一组N个数,每次从中抽出一个数(第一和最后一个不能抽),该次的得分即为抽出的数与相邻两个数的乘积。直到只剩下首尾两个数为止。问最小得分是多少?
http://wenku.baidu.com/view/ace70e24192e45361066f5c2.html
树型dp
A - Park Visit HDU 4607
简单的求树的直径,两次dfs 复杂的O(E)
先以任意一点遍历到最远的点,然后再从这个最远的点遍历到距它最远的点。
证明http://www.cnblogs.com/*qi/archive/2012/04/08/2437424.html
B - Computer HDU 2196
题意是求树中每个点到所有叶子节点的距离的最大值是多少。
由于对于一个节点来说,可能得到的距离最大的值的路径来自他的子树,或者从他的父节点过来,所以用两次DFS。第一次DFS求出所有节点在他的子树范围内到叶子节点距离的最大值和第二大的值,第二次DFS更新从父节点过来的情况就可以了。因为如果只存最大值的话,判断一个点的从父节点过来的最大值,那么如果他的父节点存的最大值正好是从该点过来的,那么就失去了从父节点过来的状态,所以要记录最大的两个值。
C - Power Station POJ 4045
给你一棵树,让你求一点,使该点到其余各点的距离之和最小。如果这样的点有多个,则按升序依次输出。
E - The more, The Better HDU1561
题意:n座城堡,每个里面都有宝物,要求在你可以攻占m个城堡得到的最多的宝物,但是如果要攻破一个城堡,必须要攻破它依赖的那个城堡,例如,如果a依赖b,那么如果想要攻破a就必须先攻破b。把每个城堡看作是物品,那么这个物品的城堡数量是1,价值就是宝物了。
解题思路:根据题意知道这种关系会形成一颗多叉树,根节点是0.从P=0开始,
1、遍历所有P的孩子,遇到某个孩子还有孩子,就把该节点当作P,继续1,直到遍历完所有的节点,如果P的所有孩子都没有孩子,那么转到2,存在有的孩子有孩子,转到3;
2、对P的所有孩子进行01背包,假设P的价值是Vp,P共有n个孩子,然后对每个孩子进行01背包(因为对于每个城堡只能取一次),那么可以得到城堡数量从0到n对应的最大价值(0对应的肯定是0了),用dp[i]表示,i代表城堡数量,dp[i]代表价值,接着从i=0开始,都加上P的价值Vp(因为所有的物品都依赖P),同时城堡的个数也加1;最后把得到的n+1个物品保存到P点,这样,这些物品就相当于分组背包里面的一组背包了,储存在castle里面,然后接着步骤1的遍历;
3、这个就很简单了,由于已经遍历过P所有孩子了,所以,只需要再从头开始遍历一遍所有孩子,如果遍历的孩子还有孩子,那么就把它的所有孩子当作是分组背包处理,如果遍历的孩子没有孩子,那么就当01背包处理,最后会得到一个城堡数量从0到m(也就是题目给的城堡数量的上限,以为不确定P的所有孩子的个数,所以就用最大的m)对应的最大价值,这个时候,如果P是0,那么就输出dp[m]就好了,如果不是,像2一样,dp[i]代表的价值都加上P的价值,数量也加1,也储存在castle里面,继续步骤1的遍历;
这题其实就是经典的《选课》问题
状态 dp[i][j] 为以 i 为根节点,选出 j 个节点的最大价值(包括 i 这个节点)
转移方程:dp[i][j]=max(dp[i1][j1]+dp[i2][j2]+....+dp[ik][jk])+a[i] j1+j2+...+jk=j-1
由于这个题目上的树并非一棵树而是一个森林,因此我们通过把根设为0使其变成真正意义上的树
那么答案:dp[0][m+1]
看到这个转移方程可能一时难以下手,因为你当前的状态转移是要搞定子节点的各个分支上的情况的
即子节点上各个分支分别取了多少个物品,才能使得决策最优
而对于这个问题,我们可以发现与经典的背包问题存在着相似性,这里的 j 其实就是背包的容量
因此对于这个子问题,我们可以用背包来取最优
记 dp0[i][j] 为以 i 为根节点,它的分支中取出 j 个节点的最大价值
那么转移方程就是经典的背包
dp0[i][j+k]=max{dp0[i][j+k],dp0[i][j]+dp[son[i]][k]}
即我们在一个分支中最优的取出 k 个物品后,从 j 转移到了 j+k
从这里我们也可以看出
本题的DP状态和子问题背包的DP状态其实两个是紧密相连的
两者的区别也仅仅在于本题的DP状态对于以 i 为根节点的子节点情况,它是包括 i 这个根节点本身的
而背包的状态则是不包括在内的,而就是这样的区别导致它们在具体转移的时候方程是很不一样的
实现的一些细节:
1:图用邻接表来存储,偷懒的方式是用一个 二维 vector
2:树形DP中表示某个点没有访问应该把它设置成 -1
3:用记忆化搜索实现比较容易而且高效,因为记忆化搜索只是计算了那些我们需要的状态,而递推来搞的话,往往把所有的状态都给算出来了
开始让我难以理解的是dp0的那个方程,看了很多人的博客后才算明白了(联系最上面那个dp的转移方程,dp0的工作就是将容量为j的i的分支做背包,这里应该还有一维n表示i的前n个子节点,因为容量j从大到小循环故省略,让我开始始终没有理解···)
状态压缩
A - Little Kings SGU223
B - 炮兵阵地 POJ 1185
记忆化搜索
这是放棋子的模型,因为攻击范围,所以要保存两行的状态,枚举。
f[i][s1][s2]表示第i行的状态为s1,i-1行为s2的时候前i行已经放置的炮的个数,那么f[i][s1][s2]=s2的状态1的个数+max(f[i-1][s2][jj]),只要枚举jj,且jj是合法的。我是倒着做的。