preparing

2023“钉耙编程”中国大学生算法设计超级联赛(9)- 1003 Reasoning 题解

题目翻译

基本符号

有一推理系统,其中有这些符号:

  • 括号 \((\)\()\)
  • 逻辑连接词 \(\lnot\)\(\rightarrow\)
  • 全称量词 \(\forall\)
  • 变量 \(u\sim z\)
  • 常量 \(a\sim e\)
  • 函数 \(f\sim h\)
  • 谓词 \(P\sim T\)

这些符号是构成系统的基础,他们之间能够组合出一些其他概念:

项(term)

  • 变量和常量都叫做项;
  • 假设 \(t_1,\ldots,t_n\) 都是项且 \(f\) 是函数,则 \(f\,t_1\ldots t_n\) 也是项。

也就是说,变量、常量和函数的组合就是项。

公式(formula)

  • 假设 \(t_1,\ldots,t_n\) 都是项且 \(P\) 是谓词,则 \(P\, t_1\ldots t_n\) 是公式。这种公式被称为原子公式;
  • 假设 \(\varphi\) 是公式,那么 \((\lnot \varphi)\) 也是公式;
  • 假设 \(\varphi\)\(\psi\) 都是公式,那么 \((\varphi\rightarrow \psi)\) 也是公式;
  • 假设 \(\varphi\) 是公式,\(v\) 是变量,那么 \(\forall v \varphi\) 也是公式。

公式有四种定义,其中第二三种定义只是公式间的组合。下面的定义全部都基于这四种公式的定义。

自由出现(free occurrence)

对于公式 \(\varphi\) 和变量 \(x\)\(x\)\(\varphi\) 中自由出现的定义为:

  • 假设 \(\varphi\) 是原子公式,当且仅当 \(x\)\(\varphi\) 中出现(字符 \(x\) 在字符串 \(\varphi\) 中)就称 \(x\)\(\varphi\) 中自由出现;
  • 假设 \(\varphi\) 形如 \((\lnot\psi)\),则 \(x\)\(\varphi\) 中自由出现当且仅当 \(x\)\(\psi\) 中自由出现;
  • 假设 \(\varphi\) 形如 \((\psi\rightarrow \gamma)\),则 \(x\)\(\varphi\) 中自由出现当且仅当 \(x\)\(\psi\) \(\gamma\) 中自由出现;
  • 假设 \(\varphi\) 形如 \(\forall v \psi\),则 \(x\)\(\varphi\) 中自由出现当且仅当 \(x\neq v\) \(x\)\(\psi\) 中自由出现。

可知,\(x\)\(\forall v \varphi\) 中自由出现的必要条件是 \(x\neq v\),即 \(x\)\(\forall x \varphi\) 中不可能自由出现。我们可以把 \(\forall x\) 看作是 \(x\) 被“锁住”了。

替换(replacement)

对于公式 \(\varphi\)、变量 \(x\) 和项 \(t\),替换 \(\varphi_t^x\) 定义为:

  • 假设 \(\varphi\) 是原子公式,则 \(\varphi_t^x\) 表示将 \(\varphi\) 中的每一个 \(x\) 都替换成 \(t\)
  • 假设 \(\varphi\) 形如 \((\lnot \psi)\),则 \(\varphi_t^x=(\lnot\psi)_t^x=(\lnot\psi_t^x)\)
  • 假设 \(\varphi\) 形如 \((\psi\rightarrow\gamma)\),则 \(\varphi_t^x=(\psi\rightarrow\gamma)_t^x=(\psi_t^x\rightarrow\gamma_t^x)\)
  • 假设 \(\varphi\) 形如 \(\forall v\psi\),则 \(\varphi_t^x = (\forall v\psi)_t^x = \begin{cases}\forall v\psi&x=v\\\forall v(\psi_t^x)&x\neq v\end{cases}\)

也就是说,假设 \(x\) 在公式 \(\forall x\varphi\) 中被锁住了,那么该公式的替换就是其本身。

零冲突替换(zero-contradiction replacement)

对于公式 \(\varphi\)、变量 \(x\) 和项 \(t\),零冲突替换定义为:

  • 假设 \(\varphi\) 为原子公式,\(t\) 恒能在 \(\varphi\) 中零冲突替换 \(x\)
  • 假设 \(\varphi\) 形如 \((\lnot \psi)\),则 \(t\) 能在 \(\varphi\) 中零冲突替换 \(x\) 当且仅当 \(t\) 能在 \(\psi\) 中零冲突替换 \(x\)
  • 假设 \(\varphi\) 形如 \((\psi\rightarrow \gamma)\),则 \(t\) 能在 \(\varphi\) 中零冲突替换 \(x\) 当且仅当 \(t\) 能在 \(\psi\) \(\gamma\) 中同时零冲突替换 \(x\)
  • 假设 \(\varphi\) 形如 \(\forall v\psi\),则 \(t\) 能在 \(\varphi\) 中零冲突替换 \(x\) 当且仅当满足下面条件中的任意一个:
    • \(x\)\(\varphi\) 中没有自由出现;
    • \(v\) 没有在 \(t\) 中出现,且 \(t\) 能在 \(\psi\) 中零冲突替换 \(x\)

即,\(t\) 能在 \(\forall v\varphi\) 中零冲突替换 \(x\),当且仅当 \(x\)\(\forall v\varphi\) 中不存在或被锁,或是被锁住的 \(v\) 没在 \(t\) 中出现且 \(t\) 能继续往下递归。

样例

现在给出公式 \(A\)、变量 \(x\) 和项 \(t\),再给出每个函数或谓词的参数个数,\(Q\) 次询问 \(t\) 是否能在 \(A\) 中零冲突替换 \(x\),若能还要给出替换后的 \(A_t^x\)

给出的公式中,\(\lnot\)\(N\) 代替,\(\rightarrow\)\(I\) 代替,\(\forall\)\(A\) 代替。

样例输入:

1
AxAy(PzvfxygxyI(NQ))
5
f 3
g 2
P 3
Q 0
h 2
4
hxx x
hxy y
hzz z
hxz z

样例输出:

Y
AxAy(PzvfxygxyI(NQ))
Y
AxAy(PzvfxygxyI(NQ))
Y
AxAy(PhzzvfxygxyI(NQ))
N

样例的公式 \(A = \forall x\forall y(P(z,v,f(x,y,g(x,y)))\rightarrow(\lnot Q))\)

题解

把整棵树建出来然后暴力跑即可。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#define maxn 2005
using namespace std;
int T,n,m,x,Q; char a[maxn],ch,trans[maxn],repl; map<char,int> para;
struct node{char opt,var; int to[maxn],num,fa;}t[maxn]; int now=1,cnt=1;
// opt 存符号,若为全称量词则记录锁住的变量 var;to 存子节点,num 计个数;fa 存父节点
void build(int pos,int las){ // now 记录到公式 A 的哪一位,pos 记录树节点编号,las 记录父节点编号
	if(now>=n) return; if(a[now]=='A'){
		t[pos].fa=las; t[pos].opt='A'; t[pos].var=a[now+1]; t[las].to[++t[las].num]=pos;
		now+=2; build(++cnt,pos); // 把全称量词和锁住的变量统计
	}else if(a[now]=='('){
		t[pos].fa=las; t[pos].opt='('; t[las].to[++t[las].num]=pos; now++; build(++cnt,pos);
		if(a[now]==')'){now++; return;} if(a[now]=='I'){now++; build(++cnt,pos);} if(a[now]==')'){now++; return;}
        // 一定要注意判右括号
	}else if((a[now]>='f'&&a[now]<='h')||(a[now]>='P'&&a[now]<='T')){
		t[pos].fa=las; t[pos].opt=a[now]; t[las].to[++t[las].num]=pos; char res=a[now];
		now++; for(int i=1;i<=para[res];i++) build(++cnt,pos);
        // 遇到函数把所有参数统计完就返回
	}else if(a[now]=='N'){
		t[pos].fa=las; t[pos].opt='N'; t[las].to[++t[las].num]=pos; now++; build(++cnt,pos);
	}else{t[pos].fa=las; t[pos].opt=a[now]; t[las].to[++t[las].num]=pos; now++;}
}
bool free_ocr(int pos,char keyw){ // 判断是否自由出现
	if(t[pos].opt>='u'&&t[pos].opt<='z') return (t[pos].opt==keyw); if(t[pos].opt>='a'&&t[pos].opt<='e') return 0;
	if((t[pos].opt>='f'&&t[pos].opt<='h')||(t[pos].opt>='P'&&t[pos].opt<='T')||t[pos].opt=='N'||t[pos].opt=='(')
		{bool flag=0; for(int i=1;i<=t[pos].num;i++) flag|=free_ocr(t[pos].to[i],keyw); return flag;}
	if(t[pos].opt=='A'&&t[pos].var==keyw) return 0; return free_ocr(t[pos].to[1],keyw);
}
bool check(int pos){ // 判断能否零冲突替换
	if(t[pos].opt>='P'&&t[pos].opt<='T') return 1; if(t[pos].opt=='N') return check(t[pos].to[1]);
	if(t[pos].opt=='('){bool flag=1; for(int i=1;i<=t[pos].num;i++) flag&=check(t[pos].to[i]); return flag;}
	if(t[pos].opt=='A'){
		if(!free_ocr(pos,repl)) return 1; int llen=strlen(trans);
		for(int i=0;i<llen;i++) if(trans[i]==t[pos].var) return 0; return check(t[pos].to[1]);
	} return 1;
}
void output(int pos,bool on){ // 替换并输出,on 表示要不要替换(可能前面被锁了)
	if(t[pos].opt>='a'&&t[pos].opt<='e') printf("%c",t[pos].opt);
	if(t[pos].opt>='u'&&t[pos].opt<='z'){if(t[pos].opt==repl&&on) printf("%s",trans); else printf("%c",t[pos].opt);}
	if((t[pos].opt>='f'&&t[pos].opt<='h')||(t[pos].opt>='P'&&t[pos].opt<='T')||t[pos].opt=='N'||t[pos].opt=='('){
		printf("%c",t[pos].opt); for(int i=1;i<=t[pos].num;i++)
			{if(t[pos].opt=='('&&t[pos].num==2&&i==2) printf("I"); output(t[pos].to[i],on);}
        // 因为没存 I 和 ),所以要判一下
		if(t[pos].opt=='(') printf(")");
	} if(t[pos].opt=='A'){printf("A%c",t[pos].var); output(t[pos].to[1],t[pos].var==repl?0:on);}
}
int main(){
//	freopen("1.in","r",stdin);
	scanf("%d",&T); while(T--){
		para.clear(); memset(a,'\000',sizeof(a)); for(int i=1;i<=cnt;i++) t[i].num=0;
		cin>>a; n=strlen(a); scanf("%d",&m); for(int i=1;i<=m;i++){
			cin>>ch>>x; para[ch]=x; // 存函数和谓词的参数个数
		} now=0; cnt=1; build(1,0); scanf("%d",&Q); while(Q--){
			cin>>trans>>repl; if(check(1)) printf("Y\n"); else{printf("N\n"); continue;}
			output(1,1); printf("\n");
		}
	} return 0;
}
/*
AxAyAz((Q(NQx)I(QxIQx))IQy)
*/
posted @ 2023-08-16 12:20  qzhwlzy  阅读(57)  评论(0编辑  收藏  举报