经典动态规划2-----Map记录

来源:互联网 发布:soa mlc 知乎 编辑:程序博客网 时间:2024/06/07 11:53

从递归可以优化到记忆化搜索,直接看题:


对于一个只由0(假)、1(真)、&(逻辑与)、|(逻辑或)和^(异或)五种字符组成的逻辑表达式,再给定一个结果值。现在可以对这个没有括号的表达式任意加合法的括号,返回得到能有多少种加括号的方式,可以达到这个结果。

给定一个字符串表达式exp及它的长度len,同时给定结果值ret,请返回方案数。保证表达式长度小于等于300。为了防止溢出,请返回答案Mod 10007的值。

测试样例:
"1^0|0|1",7,0
返回:2
import java.util.HashMap;import java.util.Map;public class Expression {static Map<String, Integer> map = new HashMap<String, Integer>();    public int countWays(String exp, int len, int ret) {    if(map.containsKey(exp+"_"+ret))return map.get(exp+"_"+ret);        if(len == 1) {    if(Integer.valueOf(exp) == ret)return 1;    elsereturn 0;    }            char[] cs = exp.toCharArray();                int rst = 0;        for(int i=1; i<len; i+=2) {        int left0 = countWays(exp.substring(0, i), i, 0), left1 = countWays(exp.substring(0, i), i, 1);        int right0 = countWays(exp.substring(i+1), len-1-i, 0), right1 = countWays(exp.substring(i+1), len-1-i, 1);                if(cs[i] == '&') {        if(ret == 0) {        rst += left0 * right0;        rst += left0 * right1;        rst += left1 * right0;        } else {        rst += left1 * right1;        }        } else if(cs[i] == '|') {        if(ret == 0) {        rst += left0 * right0;        } else {        rst += left1 * right1;        rst += left0 * right1;        rst += left1 * right0;        }        } else {        if(ret == 0) {        rst += left1 * right1;        rst += left0 * right0;        } else {        rst += left0 * right1;        rst += left1 * right0;        }        }        }                map.put(exp+"_"+ret, rst%10007);                return rst%10007;    }}
上面如果不记录,只是递归的话会TLE


有一个整型数组A,代表数值不同的纸牌排成一条线。玩家a和玩家b依次拿走每张纸牌,规定玩家a先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,玩家a和玩家b都绝顶聪明,他们总会采用最优策略。请返回最后获胜者的分数。

给定纸牌序列A及序列的大小n,请返回最后分数较高者得分数(相同则返回任意一个分数)。保证A中的元素均小于等于1000。且A的大小小于等于300。

测试样例:
[1,2,100,4],4
返回:101
import java.util.HashMap;import java.util.Map;/* * interesting * 感觉HashMap优化的力度挺大的 */public class Cards {Map<String, Integer> map1 = new HashMap<String, Integer>();Map<String, Integer> map2 = new HashMap<String, Integer>();    public int cardGame(int[] A, int n) {    return Math.max(f(A, 0, n-1), s(A, 0, n-1));    }    // i到j先选能获得的最大private int f(int[] a, int i, int j) {if(map1.containsKey(i+" "+j))return map1.get(i+" "+j);if(i == j)return a[i];int max = Math.max(a[i] + s(a, i+1, j), a[j] + s(a, i, j-1));map1.put(i+" "+j, max);return max;}// i到j后选能获得的最大private int s(int[] a, int i, int j) {if(map2.containsKey(i+" "+j))return map2.get(i+" "+j);if(i == j)return 0;int max = Math.min(f(a, i+1, j), f(a, i, j-1));map2.put(i+" "+j, max);return max;}}

这个题我们可以很容易地把递归形式写成DP的方式

/* * dp * 因为有2个递归,所以建2张表 * 因为每个递归有两个参数,所以建一个二维数组 * 2个数组是协同建立的 */public class Cards2 {    public int cardGame(int[] a, int n) {    int[][] f = new int[n][n], s = new int[n][n];    for(int i=0; i<n; i++)f[i][i] = a[i];        for(int i=n; i>=0; i--)    for(int j=i+1; j<n; j++) {    f[i][j] = Math.max(a[i]+s[i+1][j], a[j]+s[i][j-1]);    s[i][j] = Math.min(f[i+1][j], f[i][j-1]);    }        return Math.max(f[0][n-1], s[0][n-1]);    }}



总结:


写出递归后,就考虑优化吧:


(1)提炼出可变参数


(2)看看可变参数依赖的是那些项


(3)依赖的顺序发过来就是DP求得顺序


(4)什么都不依赖的状态就先单独求出来


(5)然后用小的状态求大的状态


都是暴利递归,分析计算方向,得到DP


所以最重要的是:培养递归的感觉


0 0
原创粉丝点击