表达式期望结果的数目 动态规划

1、问题描述:来源《算法与数据结构最优解》左程云著

给定一个只由0(假)、1(真)、&(逻辑与)、|(逻辑或)、^(异或)五种字符组成的字符串express,再给定一个布尔值desired。返回express能有多少种组合方式,可以达到desired的结果。


2、输入输出

express="1^0|0|1",desired=false。只有1^((0|0)|1)和1^(0|(0|1))的组合可以得到false,返回2。

express="1",desired=false。没有组合可以得到false,返回0.

输入错误输出-1


3、问题解析

1)这个问题是一个“加括号”问题,和矩阵连乘的计算过程类似,但是要比后者复杂很多。

2)问题的难点在于找出“状态”和“递推式”,因为状态不是唯一的,是和输入的desired有关系。

3)但是我们可以确定的是,只有两种状态,那就是表达式为真的数目和表达式为假的数目,令t[i][j]为从 i 到 j 表达式为真的数目,f[i][j]为表达式为假的数目。

4)初始状态:i==j,如果express[j]==1,那么t[j][j]=1;f[j][j]=0;如果express[j]==0,t[j][j]=0;f[j][j]=1。

      递推状态:i!=j,t[i][j]和f[i][j]的计算之间有依赖的关系,而且和具体的运算符也有关系,拿“|”(或运算)来举例,真=真*真+真*假+假*真,假=假*假(或运算法则),也就是t[i][j]=t[i][k]*t[k+2][j]+t[i][k]*f[k+2][j]+f[i][j]*t[k+2][j],其中i<=k<j,k+=2。

5)这个题目的思路和纸牌博弈问题类似,都是两个状态,而且互相对立,互相依赖。


4、代码如下

#include<iostream>
#include<vector>
#include<string>

using namespace std;
int getResult(string, string);
int main() {
	string str,desired;
	cin >> str >> desired;
	cout << getResult(str, desired) << endl;
}
int getResult(string str, string desired) {
	int len = str.length();
	//判断字符串是否有效
	if (len % 2 == 0)
		return -1;
	int i, j, k;
	vector<vector<int>> t(len, vector<int>(len)), f(len, vector<int>(len));//t[i][j]代表从str[i]到str[j]值为true的个数,f[i][j]代表为false的数目
	for (j = 0; j < len; j += 2) {
		if (str[j] == '1') {
			t[j][j] = 1;
			f[j][j] = 0;
		}
		else if (str[j] == '0') {
			t[j][j] = 0;
			f[j][j] = 1;
		}
		else return -1;
		cout << j << "行 " << j << "列 真:" << t[j][j] << "  假: " << f[j][j] << endl;
		for (i = j - 2; i >= 0; i -= 2) {//计算的顺序是先计算列,再从下向上
			for (k = i; k < j; k += 2) {//k从i遍历到j,类似于矩阵连乘,这个是一个累加
				//cout << "K的值:" << k << endl;
				switch (str[k + 1]) {
				case '|':t[i][j] += t[i][k] * (t[k + 2][j] + f[k + 2][j]) + f[i][k] * t[k + 2][j];
					f[i][j] += f[i][k] * f[k + 2][j];
					break;//如果是或,那么只要两边有任意一个真,那么结果就是真;用乘法的原因是假设左边有m种组合,右边有n种组合,那么总的组合方案数目就是m*n种
				case '&':t[i][j] += t[i][k] * t[k + 2][j];
					f[i][j] += f[i][k] * (t[k + 2][j] + f[k + 2][j]) + t[i][k] * f[k + 2][j];
					break;
				case '^':t[i][j] += t[i][k] * f[k + 2][j] + f[i][k] * t[k + 2][j];
					f[i][j] += t[i][k] * t[k + 2][j] + f[i][k] * f[k + 2][j];
					break;
				default: return -1;
				}
			}//end for k
			cout << i << "行 " << j << "列 真:" << t[i][j] << "  假: " << f[i][j] << endl;
		}//end for i
	}//end for j
	return desired == "false" ? f[0][len - 1] : t[0][len - 1];
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值