【题目链接】 http://poj.org/problem?id=3678
【题目大意】
有一些变量,现在给出一些他们做AND,OR,或者XOR的结果(1或0),
问这些变量是否存在满足所有结果的解集
【题解】
每个变量只有两种取值,0和1,所以我们拆点做2-SAT,
i表示xi取0,i+N表示xi取1
我们发现对于AND起来等于0的情况,两个变量至少有一个要是0,
那么就有一个变量为1那么另一个变量必须为1的条件,
对于AND起来等于1的情况我们发现两个变量必须取1,
所以要否决取0的情况,我们连边i->i+N,这样我们发现变量i如果被选中,那么一定是无解的,
这样就可以排除xi取0的情况。
OR的两种情况和AND恰好相反,处理方式一致。
对于XOR,如果结果为0,那么表示两个变量是状态相同,那么我们正反点之间均连双向边
如果结果为1,那么我们将两个变量相反状态之间连双向边。
最后求SCC判定连接情况即可。
【代码】
#include <cstdio> #include <algorithm> #include <vector> #include <cstring> using namespace std; const int MAX_V=10000; int V; //顶点数 vector<int> G[MAX_V]; //图的邻接表表示 vector<int> rG[MAX_V]; //反向图 vector<int> vs; //后序遍历 bool used[MAX_V]; int cmp[MAX_V]; //所属强连通分量的拓扑序 void add_edge(int from,int to){ G[from].push_back(to); rG[to].push_back(from); } void dfs(int v){ used[v]=1; for(int i=0;i<G[v].size();i++){ if(!used[G[v][i]])dfs(G[v][i]); }vs.push_back(v); } void rdfs(int v,int k){ used[v]=1; cmp[v]=k; for(int i=0;i<rG[v].size();i++){ if(!used[rG[v][i]])rdfs(rG[v][i],k); } } int scc(){ memset(used,0,sizeof(used)); vs.clear(); for(int v=0;v<V;v++){if(!used[v])dfs(v);} memset(used,0,sizeof(used)); int k=0; for(int i=vs.size()-1;i>=0;i--){ if(!used[vs[i]])rdfs(vs[i],k++); }return k; } int N,M; int x,y,c; char op[10]; int solve(){ V=N*2; // 0~N-1 表示 x取0 // N~N+N-1 表示 x取1 for(int i=0;i<M;i++){ scanf("%d%d%d%s",&x,&y,&c,op); if(op[0]=='A'){ if(c==0){ add_edge(y+N,x); add_edge(x+N,y); }else{ add_edge(x,x+N); add_edge(y,y+N); } }else if(op[0]=='O'){ if(c==0){ add_edge(x+N,x); add_edge(y+N,y); }else{ add_edge(x,y+N); add_edge(y,x+N); } }else if(op[0]=='X'){ if(c==0){ add_edge(x,y); add_edge(y,x); add_edge(x+N,y+N); add_edge(y+N,x+N); }else{ add_edge(x,y+N); add_edge(y,x+N); add_edge(x+N,y); add_edge(y+N,x); } } }int n=scc(); int flag=1; for(int i=0;i<N;i++)if(cmp[i]==cmp[i+N])flag=0; puts(flag?"YES":"NO"); } int main(){ while(~scanf("%d%d",&N,&M)){ solve(); }return 0; }