经典动态规划总结

本文持续更新……

1 给定两组序列 求上下匹配的最大值(POJ1692 Crossed Matchings)

题意:给出两行数,求上下匹配的最多组数是多少。

匹配规则:

1 匹配对的数字必须相同

2 每个匹配必须有且只能有一个匹配与之相交叉,且相交叉的两组匹配数字必须不同

3 一个数最多只能匹配一次

思路:

dp[i][j]表示上面取i个数,下面取j个数的最大匹配数

1)若上面不匹配或下面不匹配,则dp[i][j] = max(dp[i - 1][j], dp[i][j - 1] )

2)a[i] != b[j], i向左找最大k1使b[j] = a[k1],j向左找最大k2使a[i] = b[k2],

dp[i][j] = max(dp[i][j], dp[k1 - 1][k2 - 1] + 2 )

2 过河问题(POJ1700 Crossing River)

题意:每次过桥的时候最多两个人,如果桥这边还有人,那么还得回来一个人(送手电筒),求最短时间。

思路:

首先,每次让最快的人回来是错的。

先按时间递增排序,dp[i]表示前i个人过河的最短时间

1)若有i - 1个人过河,则让最快的人将手电筒送回,时间为dp[i - 1] + t[0] + t[i]

2)若有i - 2个人过河,让最快的人把手电筒送过来,然后第i个人和另外一个人一起过河,由于花费时间最少的人在这边,所以下一次送手电筒过来的一定是花费次少的,送过来后花费最少的和花费次少的一起过河,时间为dp[i - 2] + t[0] + t[i] + 2 * t[1]

dp[i] = min(dp[i - 1] + t[0] + t[i], dp[i - 2] + t[0] + t[i] + 2 * t[1])

3 括号匹配问题

POJ2955 Brackets

区间dp,dp[i][j]表示从ij匹配的最大数,则

1)若第i,j可匹配,dp[i][j] = dp[i+1][j-1] + 2

2)枚举中间点k(i <= k < j), dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j])

求这两者最大值。

4 方块消除

POJ1390 Blocks

题意:一排带有颜色的砖块,每一个可以消除相同颜色的砖块,,每一次可以到块数k的平方分数,求分数最大值

思路:dp[i][j][k]表示从第i到第j个段, 并且第j个段后面还有k个不与j相邻但颜色与j相同的方块数。

记忆化搜索:

1)i = j, dp[l][r][k] = (len[l] + k) * (len[l] + k)

2)直接消除,dp[l][r][k] = dfs(l, r - 1, 0) + (len[r] + k) * (len[r] + k)

否则枚举i(l <= i < r),使第i块与第r颜色块相同,dp[l][r][k] = max(dp[l][r][k], dfs(l, i, len[r] + k) + dfs(i + 1, r - 1, 0))

5 背包问题(HDU3033 I love sneakers!)

题意:给定n个集合,每个集合有一定数量的元素,取每个元素有一定的代价和价值,每个集合至少取一个元素,可取多个元素,但每个元素最多取一次,给定总的代价m,求不超过m的情况下最大价值。

思路:dp[i][j]表示前i个集合,背包容量为j时的最大价值,枚举集合,集合中的每个元素,再枚举背包容量

1)不取此元素,不变

2)取此元素,此集合之前未取任何元素,且取此元素或已选过并取此元素

故状态转移方程如下:

dp[i][k] = max(dp[i][k], dp[i - 1][k - v] + w, dp[i][k - v] + w )

状态初始化为:

dp[0][j] = 0

dp[i][j] = -INF( i >= 1)

(集合从1计数)

6 回文串问题(HDU2205 又见回文)

题意:给定两个字符串(可能为空串),求这两个串交叉组成新串的子串中的回文串的最大长度。

布尔型变量dp[i][j][k][l]表示串aijbkl能否组成新串,初始化为false,则采取区间动态规划。(1计数)

for(int i = 1; i <= la + 1; i++){

            for (int j = 1; j <= lb + 1; j++){

                dp[i][i][j][j - 1] = dp[i][i - 1][j][j] = dp[i][i - 1][j][j - 1] = 1;

            }

        }

        int ans = 0;

        for(int l1 = 0; l1 <= la; l1++){

            for(int l2 = 0; l2 <= lb; l2++){

                for(int i = 1, j = i + l1 - 1; j <= la; i++, j++){

                    for(int k = 1, l = k + l2 - 1; l <= lb; k++, l++){

                        if(!dp[i][j][k][l]){

                        bool b1 = i <= j && a[i] == a[j] && dp[i + 1][j - 1][k][l];

                        bool b2 = k <= l && b[k] == b[l] && dp[i][j][k + 1][l - 1];

                        bool b3 = l > 0 && a[i] == b[l] && dp[i + 1][j][k][l - 1];

                        bool b4 = j > 0 && b[k] == a[j] && dp[i][j - 1][k + 1][l];

                        dp[i][j][k][l] = b1 || b2 ||b3 ||b4;

                        }

                        if(dp[i][j][k][l]  && l1 + l2 > ans){

                            ans = l1 + l2;

                        }

                    }

                }

            }

        }

      

7 POJ1737 Connected Graph

题意:给定n个不同标号的顶点,求有多少个不同的无向简单连通图。

定义f[n]为答案,f[1] = 1, f[2] = 1

方法1

将总的数目减去不连通的数目

总的数目为2 ^ C(n, 2),计算不连通的数目方法如下:

枚举1号节点所在连通块大小为j(1 <= j < i ),此连通块节点有C(i - 1, j - 1)种选法,1号节点所在连通块有f[j]种形态,剩余i - j个节点有2^C(i - j, 2)种形态。故递推式如下:

f[i] = 2 ^ C(i, 2) - C(i - 1, j - 1) * f[j] * 2^C(i - j, 2)

方法2:

直接求连通的方案数:

f(n) = (f(k) * f(n - k) * C(n - 2 , k - 1) * ( 2 ^ k - 1)

原文地址:https://www.cnblogs.com/IMGavin/p/5636241.html