随笔分类 - DP(动态规划)
摘要:题意:吕布大战群雄,每位英雄都有自己的攻击力,防御力,还有hp(血量),吕布比较特殊,当他积累够100的经验值时他可以升级 。。升级的话加属性。这个比较公平一点,是单挑,每一位英雄轮流与吕布作战,当吕布杀死一个英雄后,可以得到一定的经验值,问吕布能不能杀死所有的英雄,如果可以的话,求出最后能剩余的最大血量。分析:这题目怎么被分到搜索专题了?哎,怎么剪枝还是超时……确实是比较裸的状态压缩DP,自底向上实现状态转移#include<iostream>#include<algorithm>using namespace std;struct hero{ int att,def
阅读全文
摘要:题意:Problem DescriptionACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝物。但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先攻克其他某一个特定的城堡。你能帮ACboy算出要获得尽量多的宝物应该攻克哪M个城堡吗?分析:与前面的倆道树形dp相比,本题目多了一个限制条件,就是选M个节点,这个很自然的联想到了背包问题,那么要如何在树上做一个01背包呢?先不考虑森林的情况,对于当前这一棵树,就是由以当前节点为根的树出现过的状态跟一棵子树上出现过的状态进行组合for(int j=t;j&
阅读全文
摘要:题意:在一棵树上选出一些点,每个点都有一个权值,使得和最大,前提是,父节点和子节点只能选一个。分析:整个代码与1054基本一样的,就是状态转移方程变了 ,因为题目加了一些限制dp[i][0]表示以i为根节点的不选i树的最大权值和dp[i][1]表示以i为根节点的选i的树的最大权值和状态转移方程:dp[i][0]+=max(dp[son[i][j]][0],dp[son[i][j]][1]),(0<j<size[i])dp[i][1]+=dp[son[i][j]][0];(0<j<size[i]) size[i]表示i节点的儿子数#include<iostream>#include&
阅读全文
摘要:嘿嘿,我的第一道树形DP题意:用最少的点覆盖整棵树,看一下Sample Input 就知道题意。分析:在树上做一个动态规划【状态】:dp[i][0] 为以 i 为根节点,并且该节点不放,所需要的最少的点数dp[i][1] 为以 i 为根节点,并且该节点放,所需要的最少的点数【转移方程】:dp[i][0]=sum(dp[son[i][j]][1]) 该点不放的话,那么它的儿子节点必须都放,这样之间的边才可以被覆盖dp[i][1]=sum(min(dp[son[i][j]][0],dp[son[i][j]][1])) 该点放的话,那么它的儿子节点就有两种决策,一种是放,一种是不放,取 min 就行
阅读全文
摘要:与一般的背包问题不同,要求的是第K优解,首先,我们要搞清楚的一个问题就是最优解是怎么求出来的。对于求最优解的情况,我们对每一种状态只保存了该状态下的最优解,忽略了其他解,进而实现状态之间的转移,而对于求第K优解的情况呢?其实只需要保存每一种状态下的前K优解,从这K个状态进行状态间的转移,同时去重,保存当前状态的K优解即可。#include<iostream>#include<algorithm>#include<set>using namespace std;int v[101],w[101];int n,m,k,dp[1010][31];int tmp[6
阅读全文
摘要:看完背包九讲的多重背包之后,这题目应该可以轻松做出来了模型:有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。方法:基本算法这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}复杂度是O(
阅读全文
摘要:题意:如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。分析:要使卡上的余额最少,相当于用有限的金额去买最多的菜。而题目多了一个限制条件,就是“卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)”,也是就是说,当卡上的金额大于等于5时,可以用5元去买任意价格的菜,所以当然是用这5元去买最贵的菜了,剩下的问题就是,求剩下的m-5
阅读全文
摘要:hdu3033 题目要求与普通的组合背包(每组至多选一个)有所区别,而本题目的话,每组至少选一个,那么如何保证每组至少选一个呢,问题就在初始化的问题还有状态转移的问题了if(f[i][l-br[i].b[j]]!=-1 && f[i][l]<f[i][l-br[i].b[j]]+br[i].c[j])//从当前组再多选一个 f[i][l]=f[i][l-br[i].b[j]]+br[i].c[j];if(f[i-1][l-br[i].b[j]]!=-1 && f[i][l]<f[i-1][l-br[i].b[j]]+br[i].c[j]) //在上一
阅读全文
摘要:描述在生物学中,一些生物的结构是用包含其要素的大写字母序列来表示的。生物学家对于把长的序列分解成较短的序列(即元素)很感兴趣。如果一个集合 P 中的元素可以通过串联(元素可以重复使用,相当于 Pascal 中的 “+” 运算符)组成一个序列 S ,那么我们认为序列 S 可以分解为 P 中的元素。元素不一定要全部出现(如BBC就没有出现)。举个例子,序列 ABABACABAAB 可以分解为下面集合中的元素:{A, AB, BA, CA, BBC}序列 S 的前面 K 个字符称作 S 中长度为 K 的前缀。设计一个程序,输入一个元素集合以及一个大写字母序列 S ,设S'是序列S的最长前缀,
阅读全文
摘要:题意:用N个点组成一棵深度为K的二叉树,求一共有几种方法?分析:哎,感觉自己好水呀,借鉴了USACO上面的题解才过的,自己想了好久都没什么思路设dp[i,j]表示用i个点组成深度最多为j的二叉树的方法数,(注意,这里i必定是奇数),则:dp[i,j]=∑(dp[k,j-1]×dp[i-1-k,j-1])(k∈{1..i-2})边界条件:dp[1,i]=1我们要求的是深度恰好为K的方法数S,易知S=dp[n,k]-dp[n,k-1]。但需要注意的是,如果每次都取模,最后可能会有dp[n,k]<dp[n,k-1],所以可以用S=(dp[n,k]-dp[n,k-1]+v) mod v
阅读全文
摘要:1058 题意:整体思想是利用已经知道的序列中的元素根据规则去生成新的元素, 如x * 2, x * 3, x * 5, x * 7.假设使用数组a[MAX]进行存储这一序列的所有元素, 首先使a[0] = 1, 表示第一个元素是1, 然后利用4个指针(不是内存指针,呵呵), i2 = i3 = i5 = i7 = 0. 每次我们比较 a[i2] * 2, a[i3] * 3, a[i5] * 5, a[i7] * 7 这四个元素的大小, 把最小的放到序列中, 并把对应的指针+1, 直到生成需要的个数.#include<stdio.h>#include<string.h>
阅读全文
摘要:题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3811题意:给定1~N个数,求出至少满足一个条件的排列总数m个条件如下:Ai 个数为Bi (0<i <m)分析:“本意是一个复杂的容斥原理,但是开场不久被秒杀,才发现又一个比较简单的状态DP方法” 这是武大预赛解题报告上的原句。确实,用容斥原理来解决确实十分蛋疼,最直接就是满足各个条件之后去重,好SB的样子==!。可是用状态DP却很好解决了。我们通过求出一个条件都不满足的排列总数,从而间接的求出满足至少一个条件的排列总数。对于当前的位置,利用前面已经求得的种数求得。结合代码比较好解释,看
阅读全文
摘要:hdu 1267 下沙的沙子有几粒?参考自大牛http://www.wutianqi.com/?p=2642分析,这题其实是H和D的组合排列问题,只不过要考虑期间累计的H和D的数量关系。用DP来做,可以推导出:dp[i][j] = dp[i-1][j] + dp[i][j-1]dp[][]前一个表示H的数量,后一个表示D的数量。分上面那种情况是因为最后一个必然是H或者D,而此时可以考虑把新加的一个放在最后,因为假如加的是H,如果加在[i-1][j]中加入H,则最后一个依然是H或D,此时如果成立,那么依然属于[i-1][j]或[i][j-1]的情况。所以推导出此递推关系。#include<
阅读全文
摘要:题意:小孩子玩积木堆房子的情景:每组测试数据第一行给出n,代表接下来有n块砖;接下来n行,每行给出砖的长,宽,高,属性(a,b,c,d);属性d:d=0:该砖的长度和宽度(a,b)要比垫在他下面的砖的长,广大或者相等;就是长>=长,宽>=宽d=1:改砖的宽度和长度要比下面的砖的长度大或者相等,同时,该砖的宽度值和面积值要比下面的砖的面积值大;即长>=长,宽>=宽,面积>面积。也就是长>=长&&宽>=宽&&(长>长||宽>宽)d=2:该砖的长度和宽度(a,b)要比垫在他下面的砖的长,广大(严格大于);长>
阅读全文
摘要:这倆道题目都是求最长递增子序列,只不过各自都有点点区别hdu 1025题意:•有两行点, 每行n个. 第一行点和第二行点是一一对应的, 有线连接, 如下图所示 •选择尽量多的线, 两两不交叉分析:•设与第1行第i个点对应的是第2行第f[i]个点 •假设i<j, 两条线(i, f[i])和(j, f[j])的充要条件是f[i]<f[j], 因此问题变成了 求f的最长递增子序列了hdu1025#include<iostream>#include<algorithm>#define MAXN 500010using namespace std;int a[MAXN
阅读全文
摘要:这倆道是一样的题目,题意就是将一个字符串转变为回文串的最少添加数有俩种方法:1.求正串和反串的最长公共子序列,再用原先串的长度相减即可;2.令c (i,j)表示将子串aiai+1…aj变成回文词的最小添加字符数。则这此问题就是要求c(1,n)。 •c(i,j)满足如下递推关系: 从代码可以看出,俩种方法的效率想差很大第一种方法#include<iostream>#include<string>#include<algorithm>using namespace std;int main(){ char s1[5010],s2[5010]; int s[2][
阅读全文
摘要:都是同一类型的题目,求最长公共子序列pku1458#include<iostream>#include<string>#include<algorithm>using namespace std;int main(){ char s1[1000],s2[1000]; int s[1000],t[1000],l1,l2; while(scanf("%s %s",s1,s2)==2) { l1=strlen(s1); l2=strlen(s2); memset(s,0,sizeof(s)); memset(t,0,sizeof(t)); fo
阅读全文
摘要:题目分析:•这一题是LCS,只是一个变形而已,有三种情况 (dist[ ][ ]为两符号所得的分数):•1、s1取第i个字母,s2取“ - ”:dp[i][j] = dp[i-1,j] + dist[s1[i],'-'];•2、 s1取“ - ”,s2取第j个字母:dp[i][j] = dp[i,j-1] + dist['-',s2[j]];•3、 s1取第i个字母,s2取第j个字母:dp[i][j] = dp[i-1,j-1] + dist[s1[i],s2[j]]•即dp[i,j]=max( dp[i-1,j] + dist[s1[i],'-'
阅读全文
摘要:额,比较基础的模板题,求最长公共子序列这里用滚动数组来保存中间值,因为只需要保存相邻俩行的值.pku的1936只需要加一步判断即可,判断最长公共子序列长度是否等于s1 的长度递推公式为:if(s1[i]==s2[j]) c[i,j]=c[i-1,j-1]+1;else c[i,j]=max(c[i-1,j],c[i,j-1])pku1936#include<iostream>#include<string>#include<algorithm>#define maxn 100005using namespace std; char s1[maxn],s2[m
阅读全文
摘要:郁闷死啦,一道简单的DP,搞了好几个小时,结果输出错了,反序了,都怪测试例子是三个数,反序了也不知道呀,也怪自己……………………#include<iostream>#include<string>using namespace std;int p[105],f[105],dp[105],ans[105];bool map[105][105];int main(){ int a,b,n,m,cas,t=0; cin>>cas; while(cas--) { cin>>n; for(int i=1;i<=n;i++) cin>>p[
阅读全文

浙公网安备 33010602011771号