• 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还怎么知道是哪个变量,看来还是用循环好,毕竟对题目有针对性。


  • 相关阅读:
    NCPC2016
    2016 ACM-ICPC CHINA-Final
    2016沈阳区域赛题解
    NAIPC2016部分题解
    Gym
    数论之莫比乌斯反演
    2018 多校 HDU
    LightOJ
    LightOJ
    java操作XML---XML基础知识
  • 原文地址:https://www.cnblogs.com/DOLFAMINGO/p/7538784.html
Copyright © 2020-2023  润新知