题解 SP147 【TAUT - Tautology】
Update 2021/04/12
更新了题目中的错别字以及 \(\text{markdown}\) 代码错误使用情况。
想法简述
这道题主要是考察表达式求值的能力。
所谓重言式就是无论变量值结果都为 \(\text{true}\),所以只要把所有情况枚举一遍就好了。
题目分析
\(C\) -- \(\operatorname{and}\)
\(D\) -- \(\operatorname{or}\)
\(I\) -- \(\operatorname{implies}\)
\(E\) -- \(\operatorname{if}\ \operatorname{and}\ \operatorname{only}\ \operatorname{if}\)
\(N\) -- \(\operatorname{not}\)
\(\operatorname{and}\) 和 \(\operatorname{or}\) 不作解释。
\(a\ \operatorname{implies}\ b\) 代表如果 \(a\) 成立,那么 \(b\) 成立,如果 \(a\) 不成立,那么 \(b\) 可能成立,换句话说就是相当于 \(\operatorname{not}\ a\ \operatorname{or}\ b\), \(\operatorname{not}\) 优先级高于 \(\operatorname{or}\)。
\(a\ \operatorname{if}\ \operatorname{and}\ \operatorname{only}\ \operatorname{if}\ b\) 代表如果 \(a\) 成立那么 \(b\) 成立,如果 \(a\) 不成立那么 \(b\) 也不成立,所以相当于 \([a=b]\)。
\(\operatorname{not}\ a\),大家清楚,但是前缀表达式 \(\text{NCNpp}\),其实是应该是 \(\operatorname{not}(\operatorname{not}\ p \ \operatorname{or} \ p)\) 而不是 \(\operatorname{not}(p\ \operatorname{or} \ \operatorname{not} p)\)。
然后再刷一趟表达式求值就好了。
表达式求值
这次的表达式和以往的不太一样所以使用递归来解。
先来观察以下几种情况:
\(\text{Eab}\)
\(\text{EEabb}\)
\(\text{EEEabEabEab}\)
\(\text{EEEEabbbb}\)
第一个就肯定是 \(\text{aEb}\)。
第二个先处理 \(\text{Eab}\), 再处理 \(\text{EEabb}\),那么就是 \(\text{(aEb)Eb}\)。
第三个就是 \(\text{(aEb)E((aEb)E(aEb))}\) 了。
第四个就是 \(\text{aEbEbEbEb}\)。
不难发现,当一段的运算符个数等于变量个数减一,那么可以分成两个子问题,那么用分治的想法就可以解完这道题。
代码
int T,N,nn;
char s[120],S[120];
int vis[30],cnt;
bool checker;
bool Nt[120];
bool check(int now,int L,int R){
bool A,B; int cnt=0, tot=0;
if(L==R) return ((now>>(vis[s[L]-'a'+1]-1))&1)^Nt[L];
for(int i=L;i<=R;i++){
//tot代表数字的数目,cnt代表符号的数目,当数字和符号数目一样时就是下一次的状态,这里不做解释了
if('a'<=s[i]&&s[i]<='z') tot++;
else cnt++;
if(tot==cnt){
A=check(now,L+1,i);
B=check(now,i+1,R);
break;
}
}
switch(s[L]){
case'C':return( A&&B)^Nt[L]; //这里就是4种关系式判断了,Nt数组存的是当前这次运算是否需要加not
case'D':return( A||B)^Nt[L];
case'I':return(!A||B)^Nt[L];
case'E':return( A==B)^Nt[L];
}
}
void get_string(){
memset(Nt,0,sizeof Nt); memset(vis,0,sizeof vis); N=nn=0;
for(char ch=getchar();ch!='\n'&&ch!=EOF;ch=getchar()) s[++N]=ch;
for(int i=1;i<=N;i++) if(s[i]!='N') S[++nn]=s[i]; else Nt[nn+1]=!Nt[nn+1];
//把not单独判断可以省好多事
memset(s,0,sizeof s); for(int i=1;i<=nn;i++) s[i]=S[i]; N=nn; return;
}
int main(){
// freopen("SP147.in","r",stdin);
// freopen("SP147.out","w",stdout);
cin>>T; getchar();
while(T--){
get_string();
checker = true; cnt=0;
for(int i=1;i<=N;i++)
if('a'<=s[i]&&s[i]<='z')
if(!vis[s[i]-'a'+1])
vis[s[i]-'a'+1]=++cnt;
for(int i=(1<<cnt)-1;i>=0;i--) //枚举现在的状态
if(check(i,1,N)==false){checker=false; break;}
printf(checker?"YES\n":"NO\n");
}
return 0;
还有我的造数据的代码,这里就不解释了!
#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
void DFS(int L,int R){
if(L==R){
if(rand()&1)putchar('N');
putchar(rand()%26+'a');
return;
}
putchar(rand()&1?rand()&1?'C':'D':rand()&1?'I':'E');
int mid=(rand()%(R-L))+L; if(mid==R) mid--;
// printf("%d %d %d\n",L,mid,R);
DFS(L,mid); DFS(mid+1,R);
return;
}
int main(){
freopen("SP147.in","w",stdout);
srand(GetTickCount());
int N=rand()%9+2;
putchar('1'); putchar(10);
DFS(1,N);
return 0;
}
Made By \(\operatorname{wheneveright}\)

浙公网安备 33010602011771号