Problem B. Boolean Satisfiability
Input file: boolean.in Time limit: 3 seconds
Output file: boolean.out Memory limit: 512 megabytes
Boolean satisfiability problem (SAT) is known to be a very hard problem in computer science. In this
problem you are given a Boolean formula, and you need to find out if the variables of a given formula can
be consistently replaced by the values true or false in such a way that the formula evaluates to true.
SAT is known to be NP-complete problem. Moreover, it is NP-complete even in case of 3-CNF formula
(3-SAT). However, for example, SAT problem for 2-CNF formulae (2-SAT) is in P.
SAT is the extension of SAT problem. In this problem you need to check if it is possible, and count the
number of ways to assign values to variables. This problem is known to be #P-complete even for 2-CNF
formulae. We ask you to solve #1-DNF-SAT, which is #SAT problem for 1-DNF formulae.
You are given a Boolean formula in 1-DNF form. It means that it is a disjunction (logical or) of one or
more clauses, each clause is exactly one literal, each literal is either variable or its negation (logical not).
Formally:
⟨formula⟩ ::= ⟨clause⟩ | ⟨formula⟩ ∨ ⟨clause⟩
⟨clause⟩ ::= ⟨literal⟩
⟨literal⟩ ::= ⟨variable⟩ | ¬ ⟨variable⟩
⟨variable⟩ ::= A … Z | a … z
Your task is to find the number of ways to replace all variables with values true and false (all occurrences
of the same variable should be replaced with same value), such that the formula evaluates to true.
Input
The only line of the input file contains a logical formula in 1-DNF form (not longer than 1000 symbols).
Logical operations are represented by ‘|’ (disjunction) and ‘~’ (negation). The variables are ‘A’ … ‘Z’
and ‘a’ … ‘z’ (uppercase and lowercase letters are different variables). The formula contains neither
spaces nor other characters not mentioned in the grammar.
Output
Output a single integer — the answer for #SAT problem for the given formula.
Examples
SAT问题的变种,巴拉巴拉一堆介绍问题的…图论的2—SAT没学导致我有点慌,而且没带字典看不懂题目,以为很难,结果看懂了如此简单….
题目大意:给定一串字符串,| 表示或,~ 表示非,以| 为间隔有很多种输入的变量,每种大小写字符表示一种变量,值可以使true或者false,你的任务就是将每个变量赋true或者false,有多少种可能让表达式为真.
思路:记录每种字符和它的相反情况是否出现过,有相同情况出现,两个变量可以合并,总数不加;有相反的情况出现,将flag置为1,总数也不加。
最后,如果flag==0,ans=2的总数次方-1;如果flag==1,ans=2的总数次方
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1010;
bool vis[52];//前52个为大写字符出现情况,后52个为小写字符
bool o_vis[52];//相反方向的
bool flag;//判断有没有正反两种情况都存在的字母
char c[maxn];
typedef long long ll;
int main()
{
while(scanf("%s",c)!=EOF)
{
memset(vis,false,sizeof(vis));
memset(o_vis,false,sizeof(o_vis));
flag=false;
int tot=0;
char *temp=strtok(c,"|");
while(temp)
{
if(temp[0]>='A'&&temp[0]<='Z')
{
int x=temp[0]-'A';
if(vis[x]==false&&o_vis[x]==false)
{
tot++;
vis[x]=true;
}
if(o_vis[x]==true)
{
flag=true;//flag为true说明有正反同时出现的情况
}
}
if(temp[0]>='a'&&temp[0]<='z')
{
int x=temp[0]-'a'+26;
if(vis[x]==false&&o_vis[x]==false)
{
tot++;
vis[x]=true;
}
if(o_vis[x]==true)
{
flag=true;
}
}
if(temp[0]=='~')
{
if(temp[1]>='A'&&temp[1]<='Z')
{
int x=temp[1]-'A';
if(o_vis[x]==false&&vis[x]==false)
{
tot++;
o_vis[x]=true;
}
if(vis[x]==true)
{
flag=true;
}
}
if(temp[1]>='a'&&temp[1]<='z')
{
int x=temp[1]-'a'+26;
if(o_vis[x]==false&&vis[x]==false)
{
tot++;
o_vis[x]=true;
}
if(vis[x]==true)
{
flag=true;
}
}
}
temp=strtok(NULL,"|");
}
// printf("%d\n",tot);
if(flag==false)
{
//printf("False\n");
ll ans=1;
for(int i=0;i<tot;i++)ans*=2;
printf("%I64d\n",ans-1);
}
else
{
//printf("True\n");
ll ans=1;
for(int i=0;i<tot;i++)ans*=2;
printf("%I64d\n",ans);
}
}
return 0;
}