poj上的dp专题
更新中...
http://poj.org/problem?id=1037
dp[i][j][0]表示序列长度为i,以j开始并且前两位下降的合法序列数目;
dp[i][j][1]表示序列长度为i, 以j开始并且前两位上升的合法序列数目;
于是我们可以得到递推方程式:dp[i][j][0] += dp[i-1][k][1] ( 1 <= k < j ), dp[i][j][1] += dp[i-1][k][0] ( k <= j <= i), 然后我们就可以从第一位开始枚举了。
http://paste.ubuntu.com/6941791/
http://poj.org/problem?id=1887
题目看了半天,就是求最长下降子序列,nlog(n)做法,不过莫名其妙的wa了好多次,不能多说。。
http://paste.ubuntu.com/6952643/
http://poj.org/problem?id=1157
dp[i][j]表示前i束花放到前j个花瓶中所获得的最大价值,于是有dp[i][j] = max(dp[i-1][j-1] + value[i][j], dp[i][j-1]) (i <= j <= m)(这是因为对于第i束花,可以选择放在第j个花瓶中,也可以选择放在前j - 1个花瓶中)
稍微要注意一下的就是初始化的问题了,由于是有序的放,因此dp[i][i] = dp[i-1][i-1] + value[i][i](1 <= i <= n)
http://paste.ubuntu.com/6953048/
http://poj.org/problem?id=1088
这道题很容易就看出要用记忆化搜索,如果周围的点的高度比当前的点的高度要低,就dfs。
http://paste.ubuntu.com/6953207/
http://poj.org/problem?id=1014
多重背包的应用,不多说。
http://paste.ubuntu.com/6954121/
http://poj.org/problem?id=1160
dp[i][j]表示前i个邮局设立在前j个村庄的最短距离和,sum[i][j]表示第i个村庄到第j个村庄建立一个邮局的距离和,显然,该邮局建立在i,j的中点位置最佳,于是我们可以得出:dp[i][j] = min(dp[i][j], dp[i-1][k] + sum[k + 1][j]) ( 1 <=k < j).而sum[i][j] = sum[i][j-1] + pos[j] - pos[(i + j) /2], 最后注意一下初始化就可以了。
http://paste.ubuntu.com/6957889/
http://poj.org/problem?id=1179
pair<long long, long long > dp[i][j]表示第i个数字到第j个数字经过运算后的最小值以及最大值,然后就需要枚举断开的位置了,并且由于是环,需要取模处理,最后就是记忆化搜索了,需要注意的就是两个最小值相乘也有可能成为最大值。
http://paste.ubuntu.com/6960005/
http://poj.org/problem?id=1141
记忆化搜索,dp[i][j]表示从第i个数字到第j个数字最少要插入的字符数,于是可以得出dp[i][j] = dp[i][k] + dp[k + 1][j](i <=k < j),并且当str[i] ==‘(’ && str[j] == ')' || str[i] == '[' && str[j] == ']'时,有dp[i][j] = dp[i + 1][j - 1];由于涉及到打印路径,需要开辟一个数组来记录每次选择的结果,然后递归求解。
http://paste.ubuntu.com/6964829/
http://poj.org/problem?id=1170
这道题要用到六进制压缩,如果第一种物品表示为state = 1,那么第二种物品表示为state = 6,并且以此类推,总状态就可表示为S = num1 * 1 + num2 * 6 + ...,然后就是用到完全背包了, 设dp[state]表示状态为state时的最小的价格。
http://paste.ubuntu.com/6969768/
http://poj.org/problem?id=1191
刘汝佳黑书上的题目,p116.
记忆化搜索,dp[x1][y1][x2][y2][k]表示左上角(x1,y1) -> (x2, y2)的区域切割k块的最小值。
1 double ans = 1LL << 60; 2 for (int i = x1; i < x2; i++) { 3 ans = min(ans, dfs(x1, y1, i, y2, k - 1) + getSum(i + 1, y1, x2, y2)); 4 ans = min(ans, dfs(i + 1, y1, x2, y2, k - 1) + getSum(x1, y1, i, y2)); 5 } 6 for (int i = y1; i < y2; i++) { 7 ans = min(ans, dfs(x1, y1, x2, i, k - 1) + getSum(x1, i + 1, x2, y2)); 8 ans = min(ans, dfs(x1, i + 1, x2, y2, k - 1) + getSum(x1, y1, x2, i)); 9 }
http://paste.ubuntu.com/6970765/
http://poj.org/problem?id=1661
调一个BUG调了一个从下午一直调到现在,最后发现原来是有一处地方发生了溢出!这种低级错误以后一定要杜绝!还是说说题目吧:
dp[i][0]表示从第i层的左端跳下所用的最短时间,dp[i][1]表示从第i层的右端跳下所用的最短时间,于是我们通过可以判断端点的位置的关系得出递推方程。
http://paste.ubuntu.com/6975857/
http://poj.org/problem?id=1745
dp[i][j]表示对前i个数字进行操作计算后MOD % k后为j, 那么如果dp[i][j]为true,则dp[i + 1][(j + num[i + 1]) % K] 和 dp[i + 1][ ( j - num[i + 1]) % K]均为true.
http://paste.ubuntu.com/6976041/
http://poj.org/problem?id=1837
和上一题差不多,都需要加一个偏移量,这里我选择偏移量为7500(假设所有砝码都挂在最右,则最大力距为20*15*25),dp[i][j]表示挂了前i个砝码,力距为j的方案数,于是我们可以得出:dp[i][j + pos[k] * g[i]] += dp[i - 1][j] ( 1 <= k <= C)。最后就是初始化的问题了,假设第一个砝码每个位置都挂一遍,于是有dp[1][pos[i] * g[1] + 7500] = 1( 1 <= i <= C).
http://paste.ubuntu.com/6979698/
http://poj.org/problem?id=1836
一道不错的题目,我们可以从左向右求一次最长上升子序列,从右向左求一次最长上升子序列,分别用Up数组和Down数组老保存,然后用dp1[i]表示1-i的最长上升序列长度,dp2[i]表示i-n的最长下降上升序列,并且用mark1[]和mark2[]数组来记录这个最长上升序列是否必须要包含i点,于是剩下的问题就直接枚举中间点就可以了。
http://paste.ubuntu.com/6980546/
http://poj.org/problem?id=1952
一道很不错的dp题,题目的意思是要求最长下降子序列的个数,这个很容易,但是有一个要求,就是如果两个最长下降子序列是一样的,就只能算一个,这个需要处理一下。
http://paste.ubuntu.com/6993356/
http://poj.org/problem?id=2137
dp[i][j][k]表示从第一头牛的j位置到第i头牛的k位置的最短距离,于是可以得出dp[i][j][k] = min(dp[i][j][k], dp[i-1][j][l] + getDist(k, l)),由于要围成圈,最后首位相连即可,需要注意初始化问题。
http://paste.ubuntu.com/6993710/
http://poj.org/problem?id=2184
dp[i][s]表示前i头奶牛聪明度为s是的最大的快乐度,这就类似与01背包的处理方法了,但是由于体积可以为负,这时我们需要加一个偏移量,并且处理的时候从小到大进行处理.
http://paste.ubuntu.com/6997548/
http://poj.org/problem?id=2241
一块砖一共有6中摆放方式,dp[i]表示使用前i块砖所能达到的最大的高度,于是有dp[i] = max(dp[i], dp[j] + node[i].z) (0 <= j < i) 赋初值的时候要注意,当只放一块砖的时候,是每种情况都可以的(一开始没注意到,wa了好多次)。
http://paste.ubuntu.com/6999070/
http://poj.org/problem?id=2264
本题的关键之处就是求出最长公共子序列并且用另外一个数组来标记,然后就是递归输出最后的ans。
http://paste.ubuntu.com/6999927/
http://poj.org/problem?id=2353
dp[i][j]表示到第i层第j个房间的最小值,于是有dp[i][j] = min(dp[i- 1][j], dp[i][j - 1], dp[i][j + 1]) + num[i][j].然后分别从左向右dp和从右向左dp,同时记录路径就可以了,最后遍历最后一行找值最小的列,递归输出即可。
http://paste.ubuntu.com/7009043/
http://poj.org/problem?id=3612
dp[i][j]表示第i个高度为j时前i个的最小值,很容易想到这样一个递推关系式:dp[i][j] = min(dp[i-1][k] + abs(k-j) * C + (num[i] - j) * (num[i] - j));可毫无疑问,复杂度太高。。。只能想办法优化一下:
对于k >= j有:dp[i][j]=j*c+(a[i]-j)*(a[i]-j)+min(dp[i-1][k]-k*c) (1<=k<=j)
对于k < j 有:dp[i][j]=-j*c+(a[i]-j)*(a[i]-k)+min(dp[i-1][k]+k*c) (j<k<=m)
令low[j]表示min(dp[i-1][k]-k*c)(1<=k<=j) high[j]=min(dp[i-1][k]+k*c)(j<k<=m)
于是可以预处理出这两个数组,在转移的时候就能够做到O(1)了,这样也就降低复杂度了。
http://paste.ubuntu.com/7009907/
http://poj.org/problem?id=2385
dp[i][0][j]表示到第i秒在1号树下返回j次的最大值,dp[i][1][j]表示到第i秒在2号树下返回j次的最大值。然后求一下递推关系即可。
http://paste.ubuntu.com/7010136/
http://poj.org/problem?id=2392
先按ai的值从小到大排,然后就是多重背包了。
http://paste.ubuntu.com/7010489/
http://poj.org/problem?id=2479
求两个不相交的连续子段的最大和,可以从左到右扫一遍,从右到左扫一遍并且分别用dp1[],dp2[]来记录,然后更新最大值即可。
http://paste.ubuntu.com/7014856/
http://poj.org/problem?id=2486
一道很不错的题目,dp[i][j][0]表示从节点i出发,能走j步,最后回到i点的最大值,dp[i][j][1]表示从i点出发走j步最后停留在i的某棵子树上点最大值,于是可以得出一下三种情况:
dp[0][s][j+2]=Max(dp[0][s][j+2],dp[0][t][k]+dp[0][s][j-k]);//从s出发,要回到s,需要多走两步s- >t,t- > s,分配给t子树k步,其他子树j-k步,都返回
dp[1][s][j+2]=Max(dp[1][s][j+2],dp[0][t][k]+dp[1][s][j-k]);//不回到s(去s的其他子树),在t子树返回,同样有多出两步
dp[1][s][j+1]=Max(dp[1][s][j+1],dp[1][t][k]+dp[0][s][j-k]);//先遍历s的其他子树,回到s,s - > t, 遍历t子树,在当前子树t不返回,多走一步.
http://paste.ubuntu.com/7015701/
http://poj.org/problem?id=2559
l[i]表示大于等于h[i]的最左边的位置,r[i]表示大于等于h[i]的最右边的位置,这样可以预处理出l[],r[],然后ans = max(ans, r[i] - l[i] + 1) * h[i])( 1 <= i <= n).
http://paste.ubuntu.com/7016077/
http://poj.org/problem?id=2663
先把3行n列转成n行3列,dp[row][state]表示第i行状态为state的总数,于是dp[row][s2] += dp[row - 1][s1] 其中s1,s2两状态合法,这题与poj2411几乎一样。
http://paste.ubuntu.com/7016354/
http://poj.org/problem?id=2817
状态压缩,dp[state][i]表示当前状态为state,并且以第i个字符串开头的最大匹配数,这里的状态需要解释一下,指的是用到了那几个字符串。于是我们可以得到递推关系:
dp[state | (1 << j] = min(dp[state | (1 << j)], dp[state][i] + num[i][j]),其中num[i][j]指的是字符串i和字符串j的最大匹配数。
http://paste.ubuntu.com/7026396/
http://poj.org/problem?id=2923
状态压缩,首先预处理出哪些物品能够被两辆车一起带走,dp[state]表示当前为状态为state下所需要的最小的次数。然后就是赤裸裸的状压dp了。
http://paste.ubuntu.com/7026803/
http://poj.org/problem?id=3036
首先把六边型映射到直角坐标,对于点(x,y),能够走的6个点分别为:(x, y - 1), (x, y + 1), (x - 1, y), (x + 1, y), (x + 1, y - 1), (x - 1, y + 1)。
dp[step][x][y]表示当前在点(x,y),已经走了step步的方法数,由于走的时候坐标可能为负,于是整体平移,令(15,15)为坐标原点,dp[0][15][15] =1,那么最后要求的不就是dp[n][15][15]了。
http://paste.ubuntu.com/7030831/
http://poj.org/problem?id=3042
区间dp,dp[i][j][0]表示i到j之间已经走过,并且现在在i点的最小值,dp[i][j][1]表示现在在j点,于是对于在i点,可能从i +1 - > i, 也可能从j - i,即:
dp[i][j][0] = min(dp[i + 1][j][0] + (n - (j - i) ) * (pos[i + 1] - pos[i]), dp[i + 1][j][1] + (n - (j - i)) * (pos[j] - pos[i]));
同理对于当前在j点,也可以得到向相应的方程:dp[i][j][1] = min(dp[i][j-1][1] + (n - (j - i)) * (pos[j] - pos[j - 1), dp[i][j - 1][0] + (n - (j - i)) * (pos[j] - pos[i])).
然后需要注意的就是初始话的问题了,找到L在原序列中的位置。
http://paste.ubuntu.com/7031972/
http://poj.org/problem?id=3046
dp[i][j]表示用前i中类型组成序列长度为j的方法数,于是就有dp[i][j] += dp[i - 1][j - k] (0 <= k <= num[i]).
可以优化的地方,采用滚动数组,不过每次都必须清空,一开始没这么干,样例都过不了。
http://paste.ubuntu.com/7032709/
http://poj.org/problem?id=3132
dp[i][j]表示用j个素数得到和为n的方案数,这就类似与01背包了。
状态转移方程:dp[i][j] += dp[i - x][j - 1](其中x为素数);
http://poj.org/problem?id=3140
dp[u]表示以u为根的所有子树的和,则有ans = min(ans, (sum - dp[u]) - dp[u]).在dfs的过程中不断更新即可。
http://paste.ubuntu.com/7036461/
http://poj.org/problem?id=1185
dp[i][j][k]表示第i行状态为j,第i - 1行状态为k的最大可放置数量.
首先预处理出可行的状态,即相邻的两个1之间的距离至少要大于2。
然后就是状压dp搞搞了.dp[i][j][k] = max(dp[i][j][k], dp[i-1][k][l] + num[i])(j, k, l状态不冲突,num[i]为第i行此时能够放的个数).
http://paste.ubuntu.com/7059384/
http://poj.org/problem?id=2288
dp[i][j][s]表示当前点为j,上一点是i,当前状态为s下的最小值;
ways[i][j][s]表示当前点为j,上一点为i,当前状态为s下的相同最小值的个数.
注意一下初始化。
http://paste.ubuntu.com/7060817/