题目描述
Katu Puzzle is presented as a directed graph G(V, E) with each edge e(a, b) labeled by a boolean operator op (one of AND, OR, XOR) and an integer c (0 ≤ c ≤ 1). One Katu is solvable if one can find each vertex Vi a value Xi (0 ≤ Xi ≤ 1) such that for each edge e(a, b) labeled by op and c, the following formula holds:
Xa op Xb = c
The calculating rules are:
And | 0 | 1 | OR | 0 | 1 | XOR | 0 | 1 |
---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
Given a Katu Puzzle, your task is to determine whether it is solvable.
输入格式
The first line contains two integers N (1 ≤ N ≤ 1000) and M,(0 ≤ M ≤ 1,000,000) indicating the number of vertices and edges.
The following M lines contain three integers a (0 ≤ a < N), b(0 ≤ b < N), c and an operator op each, describing the edges.输出格式
Output a line containing “YES” or “NO”.
输入样例
4 4
0 1 1 AND
1 2 1 OR
3 2 0 AND
3 0 0 XOR输出样例
YES
分析
题目大意是给点n个点和m个命题,对于每个命题都有其真假值和对应的运算关系—AND、OR、XOR,现在求是否存在解满足以上的命题。
按照题目要求,我们按下述原则进行建图:
AND运算:
- 若a and b=0:若a=1,则b必为0,有边<a,b+n>;若b=1,则a必为0,有边<b,a+n>。
- 若a and b=1:a必为1且b必为1,有边<a+n,a>和<b+n,b>。
OR运算:
- 若a or b=0:a必为0且b必为0,有边<a,a+n>和<b,b+n>。
- 若a or b=1:若a为0,则b必为1,有边<a+n,b>;若b为0,则a必为1,有边<b+n,a>。
XOR运算
- 若a xor b=0,则有以下四种情况:
若a=0,则b必为0,即:<a+n,b+n>。
若a=1,则b必为1,即:<a,b>。
若b=0,则a必为0,即:<b+n,a+n>。
若b=1,则a必为0,即:<b,a>。 - 若a xor b=1,则有以下四种情况:
若 a = 0,则b必为1,即:<a+n,b>
若 a = 1,则b必为0,即:<a,b+n>
若 b = 0,则a必为1,即:<b+n,a>
若 b = 1,则a必为0,即:<b,a+n>
按照上述原则建完图后,用tarjan算法求得强连通分量,并判断对于问题i是否存在情况强连通分量编号scc[i]==scc[i+n],有则输出“NO”,无则输出“YES”,以下是源码。
//#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#define MAXN 2005
#define MAXM MAXN*MAXN
using namespace std;
struct Edge{
int v,next;
Edge(){}
Edge(int v,int next):v(v),next(next){}
}edge[MAXM];
int EdgeCount,head[MAXN];
int n,m,block_cnt,scc_cnt;
int dfn[MAXN],low[MAXN],scc[MAXN];
bool vis[MAXN];
stack<int> s;
void addEdge(int u,int v)
{
edge[++EdgeCount]=Edge(v,head[u]);
head[u]=EdgeCount;
}
void init()
{
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(scc,0,sizeof(scc));
memset(vis,false,sizeof(vis));
EdgeCount=block_cnt=scc_cnt=0;
}
void get()
{
for(int i=1;i<=m;i++){
char op[5];
int a,b,val;
scanf("%d%d%d%s",&a,&b,&val,op);
if(op[0]=='A'){ //AND操作
if(val==0){ //a and b=0
addEdge(a,b+n); //a为1,b为0
addEdge(b,a+n); //b为1,a为0
}
else{ //a and b=1
addEdge(a+n,a); //a必为1
addEdge(b+n,b); //b必为1
}
}
else if(op[0]=='O'){ //OR操作
if(val==0){ //a or b=0
addEdge(a,a+n); //a必为0
addEdge(b,b+n); //b必为0
}
else{ //a or b=1
addEdge(a+n,b); //a为0,b为1
addEdge(b+n,a); //b为0,a为1
}
}
else{ //XOR操作
if(val==0){ //命a xor b=0
addEdge(a+n,b+n); //a为0,b为0
addEdge(b+n,a+n); //b为0,a为0
addEdge(a,b); //a为1,b为1
addEdge(b,a); //b为1,a为1
}
else{ //a xor b=1
addEdge(a,b+n); //a为1,b为0
addEdge(b,a+n); //b为1,a为0
addEdge(a+n,b); //a为0,b为1
addEdge(b+n,a); //b为0,a为1
}
}
}
}
void tarjan(int u)
{
dfn[u]=low[u]=++block_cnt; //时间戳
vis[u]=true; //标记入栈
s.push(u);
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].v;
if(!dfn[v]){ //还没访问过
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v]) //已访问过且还在栈中
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){ //满足强连通分量
scc_cnt++;
while(1){
int tmp=s.top();s.pop();
scc[tmp]=scc_cnt; //标记所属强连通分量
vis[tmp]=false; //标记出栈
if(tmp==u)break;
}
}
}
bool TwoSat()
{
for(int i=0;i<2*n;i++) //s缩点
if(!dfn[i])
tarjan(i);
for(int i=0;i<n;i++)
if(scc[i]==scc[i+n]) //矛盾
return false;
return true;
}
int main()
{
while(~scanf("%d%d",&n,&m)){
init(); //初始化
get(); //读入数据
if(TwoSat()) printf("YES\n");
else printf("NO\n");
}
}