目录
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
//声明变量
string boolStr, str;
vector <string> ans;//ans中存放的是每个只含and的子bool表达式,在下面分析中有详细说明,这里的判断符长度可能不同,所以用vector与string组合数据结构的正好。
int False = 100, True = 1, id = 100; // 这里变量False是指向False链的语句或者直接指向False出口的变量
//True与False相反时True链的语句或True出口,id就是输出时前面的序号
getline(cin, boolStr); // 这里的getline方法获取一整行数据。
boolStr += " ed";//这里是"空格ed",不要遗漏空格
stringstream temp(boolStr);//用字符串中的空格分隔,赋给temp
while (temp >> str)
{
if (str == "and")False += 2;//这里为什么False+=2,分析中详细说明
else if (str != "or" && str != "ed")ans.push_back(str);//这里为什么判断非呢,就是向让这两条简单的先处理,复杂的放在后面
else{
if (str == "or")False += 2;
else False = 0;//else执行的是str=="ed"的情况就是最后一部分了,这一部分是特殊的,在下面分析中会详细说明
int len = ans.size();
for (int i = 0; i < len - 3; i += 3){
printf("%d(j%s,%s,%s,%d)\n", id, ans[i + 1].c_str(), ans[i].c_str(), ans[i + 2].c_str(), id + 2);
id++;
printf("%d(j,_,_,%d)\n", id, False);
False = id++;
}
//这里or左面的第一个判别式是特殊的,需要单独处理,下面分析中详细说明
printf("%d(j%s,%s,%s,%d)\n", id, ans[len - 2].c_str(), ans[len - 3].c_str(), ans[len - 1].c_str(), True);
True = id++;
printf("%d(j,_,_,%d)\n", id, False);
id++;
ans.clear();//记得清空,用来存储下一个子bool表达式
}
}
return 0;
}
淡紫色背景的字是说明的,就是这个颜色,不想看的可以跳过。红体字、黄色或淡红色背景都是强调的,方便阅读定位。
分析:
对:a < b and b < c or c < d and d > e进行分析
首先由于and和or的优先级不同,所以可以将这个大的bool表达式划分成子bool表达式,减小规模。
划分方法:就是用or将其划分成只含有and的子bool表达式,就像(a > b and b > c) or (c > d) or (d > e and e > g)。这里的划分是在while循环中对or和and的不同处理体现的。
这里为什么时用or划分而不是用and划分呢!可能有同学不知道,就简单说明一下(知道或不想知道的直接跳过这段):因为and的优先级高,or的优先级低(*^▽^*)。那为什么or的优先级低就要用or?再说明一下,如果现在and和or的优先级相同,但是如何做才能让and的优先级高。很简单,只要将所有的连续的and子bool表达式用括号括起来就可以了,就像(a > b and b > c) or (c > d) or (d > e and e > g),这样是不是发现就相当于用or将表达式划分成立只含有and的一个个子bool表达式,当然为了体现用or作为划分符号,所以c > d我也括起来了,相当于有零个and的子bool表达式。
最后我们要写出下面这样的语句,语句就是从前面第一个判别式开始写,每个判别式两条语句,最主要时最后面的跳转如何写。下面就开始说明对每个只含and的子bool表达式如何写跳转。
100(j<,a,b,102)
101(j,_,_,104)
102(j<,b,c,1)
103(j,_,_,101)
104(j<,c,d,106)
105(j,_,_,0)
106(j>,d,e,102)
107(j,_,_,105)
上述语句中的真链和假链(真为蓝色,假为黑色)。我们要写的就是像这样的两条链,这里可以看作链表,最后面的数字就是指针,这样就会发现,从任何一条语句都可以到达真出口或假出口(最后索引为1和0)。
每个只含and的子bool表达式:当从前向后遍历a < b and b < c or c < d and d > e遇到and假出口F+=2, 遇到or或ed(单独加在最后面的),说明前面已经遍历出了一个只含and的子bool表达式。如果当前遍历的是or,假出口F += 2,如果当前遍历的是ed,F置为0 。循环输出除了or左边第一个判定式以外的所有判定式,输出方式如下:ans用来存储例如a > b这些判定式,用过F(假出口)后让其等于id++。
printf("%d(j%s,%s,%s,%d)\n", id, ans[i + 1].c_str(), ans[i].c_str(), ans[i + 2].c_str(), id + 2);
id++;
printf("%d(j,_,_,%d)\n", id, F);
F = id++;
然后对or左边第一个判定式单独输出,输出方式如下:下面程序结构中有一些问题的解释。
printf("%d(j%s,%s,%s,%d)\n", id, ans[len - 2].c_str(), ans[len - 3].c_str(), ans[len - 1].c_str(), T);
T = id++;
printf("%d(j,_,_,%d)\n", id, F);
id++;
代码实现思路:
声明变量:
vector <string> ans; 用来存放每个只含and的子bool表达式。这里的判断符长度可能不同,所以用vector与string组合数据结构的正好,vector时一个容器和数组作用差不多,string就是字符串,这两个都是STL中封装的数据结构,它们还对应很多操作的方法。在STL中还封装了很多其他的数据结构和方法有兴趣的可以学习一下。
string boolStr, str;分别用来存储输入的bool表达式和后面使用的临时变量。
int False = 100, id = 100, True = 1;这里变量False是指向False链的语句或者直接指向False出口的变量,True与False相反时True链的语句或True出口,id就是输出时前面的序号,即False和True都是语句后面的指针,如:100(j<,a,b,102)这里的102就是指向True链中下一个语句。
使用到的方法:
getline()这里的getline方法可以使用cin数据流获取一整行数据,如果直接使用cin遇到空白字符就是结束。这个方法就相当于用cin循环输入,直到遇到换行符结束,然后拼接起来返回。
stringstream temp(boolStr)用空格分隔字符串boolStr赋值给temp,相当于Java中的split()。
ans.push_back()这里的ans就是上面声明的变量,在末尾压入一个字符串。
程序结构:
主要结构就是一个大的while循环,循环中对and,or和ed,以及其他,三种情况分开处理,上面说了用or分隔,所以在and,其他两种情况中不需要作什么处理。当遍历的字符是什么a呀,>呀,b呀啥的就直接压入vector <string> ans就行,但是在遇到and时为什么要False+=2呢!因为在一个,全是and的子bool表达式中一旦遇到假则整个表达式不需要再判断,这时就需要直接跳转到下一个子bool表达式(即and表达式的假出口是下一个and表达式的起点,需要每次加2寻找假出口),每遇到一个and说明前面多了两条语句,所以False+=2 。
接下来就是最复杂的对or和ed的处理,这里的ed是单独加在输入串中的,不加最后一个or后面剩下的一个子bool表达式就不会处理了。这里可不可以再加一个or呢!不建议!!如果加的是or那么每次都要判断当前遍历的or是不是最后一个呀!所以直接加一个ed比较省事。这里为什么必须判断是否是最后一个子bool表达式呢(即是否遍历到ed,如果加的是or,则判断是否遍历到最后一个or)!因为假出口一直没有确定,当表达式没有判断完最后再出现一下结果为假的情况,所以前面的都是假链上的语句,并没有假出口(语句最后索引为0)。当处理到最后一个子bool表达式时就可以将False置为0(假出口)了。
还有,在程序中一共四条输出语句,在循环外面的为什么这么特殊,它就是特殊,每一个or左边第一个判别式(不含and和or等逻辑符,只有一个>, <, ==等判定符的小式子,如a > b)是特殊的,需要单独处理。这个为什么特殊?因为,如果可以执行到这里说明前面都是真,如果这个判别式也是真,则整个表达式为真,第一次直接都真出口(1),后面再出现可以使整个表达式为真的情况则只需要跳转到真链上就可。
程序中第25行和29行中分别要False = id++和True = id++,这里就是要实现上面一段形成真链和假链(不是单链是多链,但共有一个真出口和假出口),如果把真出口和假出口(False=0和True=1)看做链表的表头,那么真链和假链的形成过程是不同的,真链是从表头形成,假链是从表尾开始。造成这样的结果是因为or的短路和我们的划分结果造成的,and的短路是有假则假,遇到假就会直接跳转到假出口,遇到真会后跳两条;or的短路是有真则真,如果有一个and子bool表达式为真直接跳转真出口。大家发现这里有“直接跳转假出口”和“直接跳转真出口”,但其实两者是不同的。and遇假则假,跳转的假出口是下一条and子bool表达式,这时不能判定整个表达式为假;or遇真则真,是跳出整个表达式,整个表达式为真。所以真链一开始就可以真到出口,假链需要遍历完才知道。比如说假链,105(j,_,_,0)这里跳向假出口,所以在下面107(j,_,_,105)语句也是跳向假出口,但最后的索引不需要再填0而是填105。
以上就是我觉得大家可能产生疑惑或者不理解的地方的解释。
错误:
这好像是不完善的,但是可以AC。。。(未完待续。。。无限延期[doge])