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

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


对于一个只由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;
    		else							return 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值