//本文针对POJ题目3295给出三种不同的思路
//算法的时间复杂度一次减小//方法一的时间超过了题目要求的1000Ms,可以当做字符串转表达式树的联系,方法二和三更好些
//题目网址
//http://poj.org/problem?id=3295
//针对本题确定永真式的枚举方法,网上搜索的结果目前只有两种:for循环(本文采用的方法)和位移运算。后者主要通过对0到31的整数进行位移运算来计算相应的pqrst
//的逻辑值,其中pqrst的高地位是固定的,现在定p为高位,t为低位,那么理论上对于Npq的输入只用检测4种情况,这样做也应该是更聪明的做法,这需要对表达式增加检
//索操作数的功能函数,方便起见,将32个整数(0~31)全部考虑一遍。定逻辑量logval=3,那么logval&1的结果就是t的值,logval>>1&1的结果就是s的值,以此类推,
//logval>>4&1的结果就是p的值(右移运算符的优先级高于按位与运算符)。
//方法一
#include<iostream>
#include<string>
#include<set>
#include<vector>
#include<map>
#include<stack>
using namespace std;
//先从右往左遍历,转表达式树,在表达式树中后序遍历计算所有情况,作出判断,最后后序遍历删除表达式树
//但是该方法的时间复杂度比较高
class TreeNode{
public:
TreeNode(char ce, int val=-1):cOpe(ce),value(val),pLeft(NULL),pRight(NULL){}
int value;
char cOpe;
TreeNode *pLeft,*pRight;
};
int BackTree(TreeNode*,map<char,int> );
bool DeletTree(TreeNode* root);
int main()
{
char cOpe[]={'p','q','r','s','t'};
set<char> scOpe(cOpe,cOpe+sizeof(cOpe)/sizeof(char));
char cOpr[]={'A','C','K','E','N'};
set<char> scOpr(cOpr,cOpr+sizeof(cOpr)/sizeof(char));
string str;
while(cin>>str&&str.compare("0")){
int flag=1;
stack<TreeNode*> skpTreeNode;
set<char> sctemp;
for(string::const_reverse_iterator it=str.rbegin();it!=str.rend();it++){
if(scOpe.count(*it)){
TreeNode *pTreeNode=new TreeNode(*it);
skpTreeNode.push(pTreeNode);
sctemp.insert(*it);
}
if(scOpr.count(*it)){
TreeNode *pTreeNode=new TreeNode(*it);
pTreeNode->pLeft=skpTreeNode.top();
skpTreeNode.pop();
if(*it!='N'){
pTreeNode->pRight=skpTreeNode.top();
skpTreeNode.pop();
}
skpTreeNode.push(pTreeNode);
}
}
TreeNode *pTreeRoot;
while(!skpTreeNode.empty()){
pTreeRoot=skpTreeNode.top();
skpTreeNode.pop();
}
map<char,int> mpOpr;
for(set<char>::iterator it=sctemp.begin();it!=sctemp.end();it++){
mpOpr[*it]=0;
}
//int isctemp=pow(2,double(sctemp.size()));
int isctemp=1;
for(int i=1;i<=sctemp.size();i++)
isctemp*=2;
for(int input=0;input<isctemp;input++){
int quo=input;
map<char,int>::iterator mit=mpOpr.begin();
for(int i=0;i<sctemp.size();i++){
mit->second=quo%2;
quo=quo/2;
mit++;
}
if(!BackTree(pTreeRoot,mpOpr)){
flag=0;
break;
}
}
if(flag)
cout<<"tautology"<<endl;
else
cout<<"not"<<endl;
DeletTree(pTreeRoot);
}
return 0;
}
int BackTree(TreeNode* root,map<char,int> mpOpr)
{
if(root->pLeft==NULL&&root->pRight==NULL)
return root->value=mpOpr[root->cOpe];
int left,right;
switch (root->cOpe)
{
case 'A': left=BackTree(root->pLeft,mpOpr);right=BackTree(root->pRight,mpOpr);
return root->value=left+right;
case 'K': left=BackTree(root->pLeft,mpOpr);right=BackTree(root->pRight,mpOpr);
return root->value=left*right;
case 'C': left=BackTree(root->pLeft,mpOpr);right=BackTree(root->pRight,mpOpr);
return root->value=((left-right)<=0?1:0);
case 'E': left=BackTree(root->pLeft,mpOpr);right=BackTree(root->pRight,mpOpr);
return root->value=((left-right)==0?1:0);
case 'N': left=BackTree(root->pLeft,mpOpr);
return root->value=!left;
}
}
bool DeletTree(TreeNode* root)
{
if(root==NULL)
return true;
if(DeletTree(root->pLeft)&&DeletTree(root->pRight)){
delete root;
return true;
}
}
//方法二
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<stack>
using namespace std;
//问题的表达式可以理解为一个逆序的后缀表达式,而且没有括号和运算符的干扰
//因此可以使用后缀表达式求值的方法
//但是需要从右往左遍历一次
int main()
{
int iOpe[]={0,0,0,0,0};
char cOpe[]={'p','q','r','s','t'};
map<char,int> mpOpe;
for(int i=0;i<5;i++)
mpOpe[cOpe[i]]=0;
string str;
while(cin>>str&&str.compare("0")){
for(int i=0;i<5;i++){
if(str.find(cOpe[i])!=string::npos)
iOpe[i]=0;
else
iOpe[i]=1;
}
int flag=1;
stack<int> sk;
for(mpOpe['p']=iOpe[0];mpOpe['p']<2&&flag;mpOpe['p']++)
for(mpOpe['q']=iOpe[1];mpOpe['q']<2&&flag;mpOpe['q']++)
for(mpOpe['r']=iOpe[2];mpOpe['r']<2&&flag;mpOpe['r']++)
for(mpOpe['s']=iOpe[3];mpOpe['s']<2&&flag;mpOpe['s']++)
for(mpOpe['t']=iOpe[4];mpOpe['t']<2&&flag;mpOpe['t']++){
for(string::reverse_iterator iter=str.rbegin();iter!=str.rend();iter++){
int left,right;
if(mpOpe.count(*iter))
sk.push(mpOpe[*iter]);
else{
switch (*iter){
case 'A':left=sk.top();sk.pop();right=sk.top();sk.pop();
sk.push(left+right);break;
case 'C':left=sk.top();sk.pop();right=sk.top();sk.pop();
sk.push((left-right)<=0?1:0);break;
case 'K':left=sk.top();sk.pop();right=sk.top();sk.pop();
sk.push(left*right);break;
case 'E':left=sk.top();sk.pop();right=sk.top();sk.pop();
sk.push((left-right)==0?1:0);break;
case 'N':left=sk.top();sk.pop();
sk.push(!left);break;
}
}
}
if(!sk.empty()&&!sk.top()){
flag=0;
}
sk.pop();
}
if(flag)
cout<<"tautology"<<endl;
else
cout<<"not"<<endl;
}
return 0;
}
#include<iostream>
#include<string>
#include<set>
#include<map>
using namespace std;
int iOpe[]={0,0,0,0,0};
char cOpe[]={'p','q','r','s','t'};
set<char> scOpe(cOpe,cOpe+5);
map<char,int> mpOpe;
//核心思路:采用递归的方法
//表达式中没有小括号,且各个运算符的优先级相同
//唯一的区别就是双目和单目运算
//一个重要的特点就是表达式的嵌套,例如:AqNq中第一个q是A的左操作数,而Nq的结果作为A的右操作数
//因此,我们可以将表达式分别为两部分:左操作数和右操作数。然后,根据左右操作数计算相应操作符的结果
//编程的关键是如何找到左右操作数----用一个迭代器足够!!!
//从调用OpeLog函数开始指向左操作数表达式(可能是单个的数或者复杂的运算式)的开头,返回时指向左操作数相应的右操作数表达式的开头
//如果指向数则返回数值,同时迭代器自加加;如果指向操作符则继续递归
//tip:算法需要检查2的5次方种输入来判断是否为tautology,这未免有些浪费,因此在循环开始之前先检查谁存在于表达式。
//存在的允许判断两次,不存在的只允许判断一次!!!
int OpeLog(string::const_iterator &beg1)
{
int left,right;
//string::iterator subStrbeg;
if(scOpe.count(*beg1)){
int temp=mpOpe[*beg1];
beg1++;
return temp;
}
switch(*beg1)
{
case 'A': left=OpeLog(++beg1);
right=OpeLog(beg1);
return left||right;
case 'C': left=OpeLog(++beg1);
right=OpeLog(beg1);
return (left-right)<=0?1:0;
case 'E': left=OpeLog(++beg1);
right=OpeLog(beg1);
return (left-right)==0?1:0;
case 'K': left=OpeLog(++beg1);
right=OpeLog(beg1);
return left*right;
case 'N': left=OpeLog(++beg1);
return !left;
}
}
int main()
{
string str;
while(cin>>str&&str.compare("0")){
for(int i=0;i<5;i++){
if(str.find(cOpe[i])!=string::npos)
iOpe[i]=0;
else
iOpe[i]=1;
}
int flag=1;
for(mpOpe['p']=iOpe[0];mpOpe['p']<2;mpOpe['p']++)
for(mpOpe['q']=iOpe[1];mpOpe['q']<2;mpOpe['q']++)
for(mpOpe['r']=iOpe[2];mpOpe['r']<2;mpOpe['r']++)
for(mpOpe['s']=iOpe[3];mpOpe['s']<2;mpOpe['s']++)
for(mpOpe['t']=iOpe[4];mpOpe['t']<2;mpOpe['t']++){
string::iterator strbeg=str.begin();
if(!OpeLog(strbeg))
flag=0;
}
if(flag)
cout<<"tautology"<<endl;
else
cout<<"not"<<endl;
}
return 0;
}