今天的题核心是区间动态规划,给一个布尔表达式圈括号。
题目如下:
给定一个布尔表达式和一个期望的布尔结果 result,布尔表达式由 0 (false)、1 (true)、& (AND)、 | (OR) 和 ^ (XOR) 符号组成。实现一个函数,算出有几种可使该表达式得出 result 值的括号方法。
【示例】输入:s = "1^0|0|1", result = 0
输出:2
解释: 两种可能的括号方法是
1^(0|(0|1))
1^((0|0)|1)
思考:
我在之前陷入了一个很大的误区,其实动态规划的最优子结构性质并不是必要元素。动态规划的一个很大作用是备忘录方法,在递归过程中减少运算量。对于今天这道题,同样的,结果是多少中方法,而参数给定了,区间加result值,所以有三个参数。
设p[i][j][r]表示从第i个数到第j个数布尔表达式运算为r(0或1)的方法数,思考当前解与前面解的关系。中断点k,由中断点的符号表示出当前解。
由底层区间为2的情况不断向上推出区间为n的情况、
s为输入布尔表达式,r为result
public :
int solution() {
int n = (int)s.size();
vector<int> nums;
string sign;
for (int i = 0; i < n; i++) {
if (!(i % 2)) {
int num = s[i] - '0';
nums.push_back(num);
}
else
{
sign += s[i];
}
}
int nlen =(int) nums.size();
//全部初始化为零?
vector< vector < vector < int > > > dp(nlen,vector < vector < int > >(nlen,vector<int>(2,0)));
for (int i = 0; i < nlen; i++) {
if (nums[i] == 1) dp[i][i][1] = 1;
else dp[i][i][0] = 1;
}
//已经记录,有nlen个数
//如何记录解决方案
//int cnt = 1, i = 0, k = 0;
for ( int cnt = 1; cnt <= nlen - 1; cnt++) {
//按照cnt递增,右端点
//就是区间为cnt的动态规划解决
for (int i = 0; i <= nlen-1-cnt; i++) {
for ( int k = i; k < i+cnt; k++) {//中间值
if (sign[k] == '|') {
dp[i][i + cnt][1] += dp[i][k][0] * dp[k + 1][i + cnt][1] + dp[i][k][1] * dp[k + 1][i + cnt][0] + dp[i][k][1] * dp[k + 1][i + cnt][1];
dp[i][i+cnt][0] += dp[i][k][0] * dp[k + 1][i+cnt][0];
}
else if (sign[k] == '&') {
dp[i][i+cnt][1] += dp[i][k][1] * dp[k + 1][i+cnt][1];
dp[i][i+cnt][0] += dp[i][k][0] * dp[k + 1][i+cnt][0] + dp[i][k][1] * dp[k + 1][i+cnt][0] + dp[i][k][0] * dp[k + 1][i+cnt][1];
}
else {
dp[i][i+cnt][1] += dp[i][k][1] * dp[k + 1][i+cnt][0] + dp[i][k][0] * dp[k + 1][i+cnt][1];
dp[i][i+cnt][0] += dp[i][k][0] * dp[k + 1][i+cnt][0] + dp[i][k][1] * dp[k + 1][i+cnt][1];
}
}
}
}
return dp[0][nlen-1][r];
}