动态规划解决布尔运算

本文介绍了一道LeetCode题目中的分治和动态规划方法,讨论了如何使用递推计算区间内运算结果为0或1的方法数,通过子数组划分和笛卡尔积合并来解决问题。
摘要由CSDN通过智能技术生成

题目:. - 力扣(LeetCode)

       本题的核心在于分治思想,即将一整个大问题的求解分治为很多小问题,最后再根据逻辑将其合并。dp[i][j][k] [i,j]区间运算结果为0或1的方法数,k=0或1,可由递推得到,初始化是对单个运算数进行初始化,为0的话dp[i][j][0]取1,反之取0。具体进行操作时按子数组长度由小到大的顺序遍历,每轮求出每个长度作为窗口在数组上滑动可以取的所有值。具体的合并过程在于将两边的所有可能情况相乘求出笛卡尔积,两边的子数组的长度都是比当前子数组长度短的子数组,在之前的循环中就已经求出了,现在刚好可以用上。

       以下是示例代码:

package main

var dp [][][]int

/*
*dp[i][j][k] [i,j]区间运算结果为0或1的方法数,k=0或1,可由递推得到
*长度由3开始到n,i = 0  ~  i = n-1,共(n+1)/2个值。
 */
func countEval(s string, result int) int {
	n := len(s)
	num := (n + 1) / 2
	dp := make([][][]int, num)
	for i := 0; i < num; i++ {
		dp[i] = make([][]int, num)
		for j := 0; j < num; j++ {
			dp[i][j] = make([]int, 2)
		}
	}

	for i := 0; i < n; i += 2 {
		if s[i] == '0' {
			dp[i/2][i/2][0] = 1
			dp[i/2][i/2][1] = 0
		} else {
			dp[i/2][i/2][0] = 0
			dp[i/2][i/2][1] = 1
		}
	}

	for len := 3; len <= n; len += 2 {
		//长度为len的子串
		for i := 0; i <= n-len; i += 2 {
			//长度为len的子串的起始位置,i+len-1 <= n-1,i <= n-len
			j := i + len - 1
			for split := i + 1; split < j; split += 2 {
				//[i,j]当中可取分隔符的位置
				switch s[split] {
				case '&':
					dp[i/2][j/2][0] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][0]
					dp[i/2][j/2][0] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][1]
					dp[i/2][j/2][0] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][0]
					dp[i/2][j/2][1] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][1]
				case '|':
					dp[i/2][j/2][0] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][0]
					dp[i/2][j/2][1] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][1]
					dp[i/2][j/2][1] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][0]
					dp[i/2][j/2][1] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][1]
				case '^':
					dp[i/2][j/2][0] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][0]
					dp[i/2][j/2][1] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][1]
					dp[i/2][j/2][1] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][0]
					dp[i/2][j/2][0] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][1]
				default:
				}
			}
		}
	}

	return dp[0][(n-1)/2][result]
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值