POJ 3678 Katu Puzzle(2-SAT)

 

【题目链接】 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;
}
posted @ 2017-04-04 15:58  forever97  阅读(120)  评论(0编辑  收藏  举报