POJ:题号 3295------tautology

//本文针对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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值