题目信息
输入命题公式的合式公式,求出公式的真值表,并输出该公式的主合取范式和主析取范式。
输入
命题公式的合式公式
输出
公式的主析取范式和主析取范式,输出形式为:mi ∨ mj ; Mi ∧ Mj
,极小项和 ∨ 符号之间有一个空格,极大项和 ∧ 符号之间有一个空格;主析取范式和主合取范式之间用;
隔开,;
前后各有一个空格。 永真式的主合取范式为 1 ,永假式的主析取范式为 0 。
输入公式的符号说明:
!
非,相当于书面符号中的 ¬
&
与,相当于书面符号中的 ∧
|
或,相当于书面符号中的 ∨
-
蕴含联结词,相当于书面符号中的 →
+
等价联结词,相当于书面符号中的 ↔
(
前括号
)
后括号
解答
#include <iostream>
#include <cmath>
#include <stack>
#include <cstring>
using namespace std;
#define N 1000
#define MAX 10000000
char x[N];//s是存放输入进去的
bool table[30] = {
false};
int explain[30] = {
0};
int value[MAX] = {
0};
int sum = 0;
int Priority(char c)
{
//符号优先级判定
switch (c)
{
case '!': return 5;
case '&': return 4;
case '|': return 3;
case '-': return 2;
case '+': return 1;
case '(': return 0;
default: return 0;
}
}
void postfix()
{
//中缀转后缀表达式,转换完后仍存放在原字符串中
char post[N] = {
'\0'};
int p = -1;
stack<char> s;
int len = strlen(x);
for (int i = 0; i < len; i++)
{
if (x[i] >= 'a' && x[i] <= 'z')
{
post[++p] = x[i];
}
else if (x[i] == '!' || x[i] == '&' || x[i] == '|' || x[i] == '-' || x[i] == '+')
{
while (!s.empty() && Priority(x[i]) <= Priority(s.top()))
{
post[++p] = s.top();
s.pop();
}
s.push(x[i]);
}
else if (x[i] == '(')
{
s.push(x[i]);
}
else if (x[i] == ')')
{
while (s.top() != '(')
{
post[++p] = s.top();
s.pop();
}
s.pop();//最后记得将(弹出
}
}
while (!s.empty())
{
post[++p] = s.top();
s.pop();
}
strcpy(x, post);
}
void settable()
{
//寻找一共有多少个元素
int len = strlen(x);
for (int i = 0; i < len; i++)
{
if (x[i] >= 'a' && x[i] <= 'z')
{
table[x[i] - 'a'] = true;
}
}
for (int i = 0; i < 26; i++)
{
if (table[i])
{
sum++;
}
}//一共有多少种组合
sum = (int) pow(2, sum);
}
int BToI()
{
//二进制转换为十进制
int ans = 0, weight = 1;
for (int i = 25; i >= 0; i--)
{
//再一次遍历所有的字母
if (table[i])
{
if (explain[i])
{
ans += weight;
}
weight *= 2;
}
}
return ans;
}
int calc(int a, int b, char c)
{
//
switch (c)
{
case '&':return a * b;
case '|':
if (a + b) return 1;
else return 0;
case '-':
if (a == 1 && b == 0) return 0;
else return 1;
case '+':return !((a + b) & 1);
default:return -1;
}
}
int work()
{
//运算真值表
stack<int> s;
int len = strlen(x);
for (int i = 0; i < len; i++)
{
if (x[i] >= 'a' && x[i] <= 'z')
{
//如果是字母的话,将当前字母取得的0或1放入运算栈中
s.push(explain[x[i] - 'a']);
}
else if (x[i] == '!')
{
//如果是非的话
if (!s.empty())
{
int tmp = (s.top() + 1) & 1;
s.pop();
s.push(tmp);
}
}
else
{
//如果是其他双元运算符的话,进入计算
int b = s.top();
s.pop();
int a = s.top();
s.pop();
s.push(calc(a, b, x[i]));
}
}
return s.top();
}
void Generate(char c)
{
//列举每个出现的元素的0,1取值情况
while (c <= 'z' && table[c - 'a'] == false)
{
//从第一个字母开始寻找
c++;
}
if (c > 'z')
{
//会得到一个二进制值,将真值表计算出的答案存储在value中
value[BToI()] = work();
return;
}
explain[c - 'a'] = 0;
Generate(c + 1);
explain[c - 'a'] = 1;
Generate(c + 1);
}
void OutputMIN()
{
int i = 0;
while (i < sum && !value[i])
{
i++;
}
if (i >= sum)
{
cout << "0 ; ";
return;
}
cout << "m" << i;
for (i++; i < sum; i++)
{
if (value[i])
{
cout << " ∨ m" << i;
}
}
cout << " ; ";
}
void OutputMAX()
{
int i = 0;
while (i < sum && value[i]) i++;
if (i >= sum)
{
cout << 1 << endl;
return;
}
cout << "M" << i;
for (i++; i < sum; i++)
{
if (!value[i])
{
cout << " ∧ M" << i;
}
}
cout << endl;
}
int main()
{
//freopen("/Users/zhj/Downloads/test.txt", "r", stdin);
cin >> x;
postfix();
settable();
Generate('a');
OutputMIN();
OutputMAX();
return 0;
}
想法
首先我们先明确题目里的含义:
一个公式的主析取范式的极小项的编码对应着该公式的所有成真赋值。
一个公式的主合取范式的极大项的编码对应着该公式的所有成假赋值。
例如a^b
二进制取值 | a | b | a^b |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 0 | 1 | 0 |
2 | 1 | 0 | 0 |
3 | 1 | 1 | 1 |
此时我们取出所有最终值为0的构成主合取范式的极大项,即M0 ∧ M1 ∧ M2
;
取出所有最终值为1的构成主析取范式的极小项,即m3
。
例如avb
二进制取值 | a | b | avb |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 0 | 1 | 1 |
2 | 1 | 0 | 1 |
3 | 1 | 1 | 1 |
取出所有最终值为0的构成主合取范式的极大项,即M0
;
取出所有最终值为1的构成主析取范式的极小项,即m1 ∨ m2 ∨ m3
。
那么此时程序可以有如下的思路
第一步,将中缀表达式转换为后缀表达式。
第二步,列举所有可能性的二进制取值,构成一张真值表。
第三步,运算出每一个真值表最后的答案。
第四步,按要求输出相应的主合取范式和主析取范式。

这里面有个运算的细节:
一、a & b
即a ^ b
等价于a * b
a | b | a & b |
a * b |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 1 | 0 | 0 |
1 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
二、a | b
即a v b
等价于if (a + b) return 1; else return 0;
a | b | a|b |
ans |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 |
1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 |
三、a - b
即a → b
等价于if (a == 1 && b == 0) return 0; else return 1;
a | b | a - b |
ans |
---|---|---|---|
0 | 0 | 1 | 1 |
0 | 1 | 1 | 1 |
1 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
四、a + b
即a ↔ b
等价于!((a + b) & 1)
a | b | a + b |
ans |
---|---|---|---|
0 | 0 | 1 | 1 |
0 | 1 | 0 | 0 |
1 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |