poj3295 Tautology —— 构造法
题目链接:http://poj.org/problem?id=3295
题意:
输入由p、q、r、s、t、K、A、N、C、E共10个字母组成的逻辑表达式,
其中p、q、r、s、t的值为1(true)或0(false),即逻辑变量;
K、A、N、C、E为逻辑运算符,
K --> and: x && y
A --> or: x || y
N --> not : !x
C --> implies : (!x)||y
E --> equals : x==y
问这个逻辑表达式是否为永真式。
PS:输入格式保证是合法的。
题解:一步一步地解决。1.首先需要记录变量,且操作时需要记录变量的值。所以就用一个ch[]记录变量,val[]记录变量的值(以val[ch[]-97]的形式记录,免去查找),并且记录变量的个数n。2.然后,变量需要“变”,然后就想到用dfs(),所以在每种情况中,变量都有确定的值。3.最后就是要处理这个逻辑表达式,一开始想直接在一个数组中处理,发现很难,要是逻辑表达式中海油逻辑表达式……,那该怎么处理。后来想到逆波兰表达式(没学,但却给了点启示),好像是用两个栈,对四则运算符和数字分开,什么遇到运算符就对数字进行操作,然后更新的,大概是这样。然后就想到:设一个主栈,初始化为输入的逻辑表达式,并设一个副栈,储存操作数。对主栈进行处理,遇到变量压入副栈,遇到操作码,提取副栈的操作数,进行操作并更新。一直到处理完主栈,最后副栈只剩下一个值。这个值就是:在变量为某些确定值时,这个逻辑表达式的真或假。
代码如下:
#include<stdio.h>//poj3295
#include<string.h>
//a为原始输入的字符串, ch用于保存变量,val用于保存变量的值
char a[105],ch[105],sm[105];
int val[30],ss[105]; int sum()
{ //sm为主栈,ss为副栈,op为操作数,rm和rs为对应的栈顶指针
int op1,op2,rm,rs = -1;
strcpy(sm,a);//初始化主栈
rm = strlen(sm);
while(--rm>=0)
{ /*对主栈进行出栈操作,如果遇到变量,则压入副栈
如果遇到操作码,则对副栈的数进行操作更新*/
if(sm[rm]>='a' && sm[rm]<='z')
{
ss[++rs] = val[sm[rm]-97];
} else
{
if(sm[rm]=='K')
{
op1 = ss[rs--], op2 = ss[rs];
ss[rs] = (op1 && op2);
} else if(sm[rm]=='A')
{
op1 = ss[rs--], op2 = ss[rs];
ss[rs] = (op1 || op2);
} else if(sm[rm]=='N')
{
ss[rs] = !ss[rs];
} else if(sm[rm]=='C')
{
op1 = ss[rs--], op2 = ss[rs];
ss[rs] = ((!op1)||op2);
} else if(sm[rm]=='E')
{
op1 = ss[rs--], op2 = ss[rs];
ss[rs] = (op1 == op2);
}
}
}
//返回最后的操作数,0或1
return ss[0];
} int dfs(int i,int n)
{ //用dfs对变量进行变化
if(i==n)
{
if(sum())return 1;
else return 0;
}
//如果出现假的情况,那肯定不是tautology, 那么余下的就不必判断,直接退出
//所以将dfs()放到判断中去
val[ch[i]-97] = 0;
if(!dfs(i+1,n))return 0;
val[ch[i]-97] = 1;
if(!dfs(i+1,n))return 0;
return 1;
} int main()
{
while(scanf("%s",a) && a[0]!='0')
{
int n = 0;
for(int i = 0; a[i]!=0; i++)
{ //记录变量
if(a[i]>='a' && a[i]<='z')
{
int j;
for(j = 0; j<n; j++)
if(a[i]==ch[j]) break;
if(j==n)
ch[n++] = a[i];
}
} if(dfs(0,n))
puts("tautology");
else
puts("not");
}
return 0;
}
看了一下网上的解题报告,可以对自己的代码进行优化:1.由于变量只有5个,所以可设5重循环来代替递归dfs(但是dfs根据变量的个数决定递归层次的,而循环则有点盲目)
2.由于变量最多只有5个,所以可直接射变量为p,q,r,s,t(但自己的方法可以是随便的变量)。3.处理逻辑表达式时,只需一个栈就够了,这个栈为上述代码的副栈。
代码如下(转载):
#include<string.h>
#include<stdio.h>
const int maxn=120;
int sta[maxn]; //数组模拟堆栈
char str[maxn];
int p,q,r,s,t;
void doit()
{
int top=0;
int len=strlen(str);
for(int i=len-1;i>=0;i--)
{
if(str[i]=='p') sta[top++]=p;
else if(str[i]=='q') sta[top++]=q;
else if(str[i]=='r') sta[top++]=r;
else if(str[i]=='s') sta[top++]=s;
else if(str[i]=='t') sta[top++]=t;
else if(str[i]=='K')
{
int t1=sta[--top];
int t2=sta[--top];
sta[top++]=(t1&&t2);
}
else if(str[i]=='A')
{
int t1=sta[--top];
int t2=sta[--top];
sta[top++]=(t1||t2);
}
else if(str[i]=='N')
{
int t1=sta[--top];
sta[top++]=(!t1);
}
else if(str[i]=='C')
{
int t1=sta[--top];
int t2=sta[--top];
if(t1==1&&t2==0) sta[top++]=0;
else sta[top++]=1;
}
else if(str[i]=='E')
{
int t1=sta[--top];
int t2=sta[--top];
if((t1==1&&t2==1)||(t1==0&&t2==0)) sta[top++]=1;
else sta[top++]=0;
}
}
} bool solve()
{ //5重循环,枚举2^5 32种可能 如果都满足 return 1
for(p=0;p<2;p++)
for(q=0;q<2;q++)
for(r=0;r<2;r++)
for(s=0;s<2;s++)
for(t=0;t<2;t++)
{
doit();
if(sta[0]==0)return 0;
}
return 1;
} int main()
{
while(scanf(%s,&str))
{
if(strcmp(str,0)==0)break;
if(solve()) printf(tautology
);
else printf(not
);
}
return 0;
}
有空再改进一下自己的代码,但仍喜欢用dfs。
等等,用dfs还怎么知道是哪个变量,看来还是用循环好,毕竟对题目有针对性。
poj3295 Tautology —— 构造法的更多相关文章
- POJ 3295 Tautology(构造法)
题目网址:http://poj.org/problem?id=3295 题目: Tautology Time Limit: 1000MS Memory Limit: 65536K Total Su ...
- POJ 3295 Tautology (构造法)
Tautology Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7716 Accepted: 2935 Descrip ...
- [POJ3295]Tautology
[POJ3295]Tautology 试题描述 WFF 'N PROOF is a logic game played with dice. Each die has six faces repres ...
- Uva 120 - Stacks of Flapjacks(构造法)
UVA - 120 Stacks of Flapjacks Time Limit: 3000MS Memory Limit: Unknown 64bit IO Format: %lld &a ...
- 利用子集构造法实现NFA到DFA的转换
概述 NFA非有穷自动机,即当前状态识别某个转换条件后到达的后继状态不唯一,这种自动机不便机械实现,而DFA是确定有限状态的自动机,它的状态转换的条件是确定的,且状态数目往往少于NFA,所以DFA能够 ...
- [Luogu4724][模板]三维凸包(增量构造法)
1.向量点积同二维,x1y1+x2y2+x3y3.向量叉积是行列式形式,(y1z2-z1y2,z1x2-x1z2,x1y2-y1x2). 2.增量构造法: 1)首先定义,一个平面由三个点唯一确定.一个 ...
- 牛客网 牛客小白月赛2 A.数字方阵-反魔方阵,梁邱构造法
天坑未补... 水一波博客,再不写博客就咸成鱼干了,只写题不写题解,过一段时间就忘了自己学过什么了. 最近重点就是把开学以来写的题补出来,没学的就滚去学会啊(= =),填一下坑... 从这篇博客开始, ...
- 紫书 习题 8-24 UVa 10366 (构造法)
又是一道非常复杂的构造法-- #include<cstdio> #include<algorithm> #define REP(i, a, b) for(int i = (a) ...
- 紫书 例题8-17 UVa 1609 (构造法)(详细注释)
这道题用构造法, 就是自己依据题目想出一种可以得到解的方法, 没有什么规律可言, 只能根据题目本身来思考. 这道题的构造法比较复杂, 不知道刘汝佳是怎么想出来的, 我想的话肯定想不到. 具体思路紫书上 ...
随机推荐
- 分布式缓存之Memcache
〇.为什么要用分布式缓存 1.软件从单机到分布式 走向分布式第一步就是解决:多台机器共享登录信息的问题. 例如:现在有三台机器组成了一个Web的应用集群,其中一台机器用户登录,然后其他另外两台机器共享 ...
- javascript 对象初探 (六)--- call()和apply()初探
在javascript中,每个函数都具有call()和apply()两个方法,您可以用她们来触发函数,并指定相关的调用参数. 此外,这两个方法还有另一个功能,就是她可以让一个对象去‘借用‘另一个对象的 ...
- 安装Vmware增强工具
主机: Win7 虚拟机: VMware8.0+Debian6 目标: 离线安装软件包和VMware Tools 在虚拟机上安装完debian6后 1.在vmware的菜单中选择Vm->inst ...
- android 打开软键盘
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools=&q ...
- python为不同的对象如何分配内存的小知识
id方法的返回值就是对象的内存地址. python中会为每个出现的对象分配内存,哪怕他们的值完全相等(注意是相等不是相同).如执行a=2.0,b=2.0这两个语句时会先后为2.0这个Float类型对象 ...
- Pixhawk之姿态解算篇(1)_入门篇(DCM Nomalize)
一.开篇 慢慢的.慢慢的.慢慢的就快要到飞控的主要部分了,飞控飞控就是所谓的飞行控制呗,一个是姿态解算一个是姿态控制,解算是解算,控制是控制,各自负责各自的任务.我也不懂.还在学习中~~~~ 近期看姿 ...
- WPA2密钥重装攻击原理分析
这两天最火爆的莫过 “WPA2被破解” 这一条大新闻了.我对其原理非常感兴趣,苦于没有找到的文献,所以就整理这么一篇,方便自己和大家理解.主要是根据目前发布的文章以及一些相关资料. 壹.WPA2的机制 ...
- Linux 网卡驱动学习(二)(网络驱动接口小结)
[摘要]前文我们分析了一个虚拟硬件的网络驱动例子,从中我们看到了网络设备的一些接口,其实网络设备驱动和块设备驱动的功能比较类似,都是发送和接收数据包(数据请求).当然它们实际是有很多不同的. 1.引言 ...
- jquery+css实现网页颜色主题变换(只改变已设置好的几种颜色主题)
又遇到颜色主题变化,这次使用了jquery+css,使用了函数传值,而不是之前网站换肤改变link的方法. 首先是设置好颜色主题后,点击改变页面颜色主题.(需要自行导入jquery.js后查看效果) ...
- c# vitural
virtual关键字用于指定属性或方法在派生类中重写. 默认情况下,派生类类从其基类继承属性和方法,如果继承的属性或方法需要在派生类中有不同的行为,则可以重写它,即可以在派生类中定义该属性或方法的新实 ...