题目大意:
n个布尔变量Xi,现在有m个约束条件,给出Xi op Xj = c,给出每个i j c 和 op。其中op可能是AND,OR,XOR。要求这n个布尔变量是否存在一组可能的取值,使得满足所有的约束条件。
分析:
题目可以转换为一个2-SAT问题。首先将每个布尔变量拆成取值为0和取值为1两个节点。给出的约束条件可以这样确定:Xi op Xj = c,那么Xi&Xj取值不等于c的那些取值就会产生冲突。例如X1 & X2 = 1,那么(X1=0,X2=0),(X1=0,X2=1)和(X1=1,X2=0)这三组取值便产生冲突。
- /*
- PKU3678 Katu Puzzle
- */
- #include <stdio.h>
- #include <memory.h>
- #define clr(a) memset(a,0,sizeof(a))
- #define N 2005
- #define M 2000005
- #define _DB if(1)
- /*************************************************/
- typedef struct StrNode{
- int j; struct StrNode* next;
- }Node;
- int memp; Node mem[M];
- void addEdge(Node* e[],int i,int j){
- Node* p = &mem[memp++]; p->j=j; p->next=e[i]; e[i]=p;
- }
- int g_DFS_First;
- void DFS_conn(Node* e[],int i,int mark[],int f[],int* nf){
- int j; Node* p;
- if(mark[i]) return; else mark[i]=1;
- if(!g_DFS_First) f[i]=*nf; //反向搜索,获取连通分量编号
- for(p=e[i];p!=NULL;p=p->next) DFS_conn(e,p->j,mark,f,nf);
- if(g_DFS_First) f[(*nf)++]=i; //正向搜索,获取时间戳
- }
- int Connection (Node* e[],int n,int con[]){
- int i,j,k,mark[N],ncon,time[N],ntime; Node *p,*re[N];
- for(clr(re),i=0;i<n;i++)for(p=e[i];p!=NULL;p=p->next) addEdge(re,p->j,i);
- g_DFS_First=1; clr(mark); clr(time);
- for(ntime=i=0;i<n;i++) if(!mark[i]) DFS_conn(e,i,mark,time,&ntime);
- g_DFS_First=0; clr(mark); clr(con);
- for(ncon=0,i=n-1;i>=0;i--) if(!mark[time[i]])
- { DFS_conn(re,time[i],mark,con,&ncon); ncon++; }
- return ncon;
- }
- /*
- 2-SAT问题
- 参数:gp[]数组传入n组节点的两个编号[0,2n-1]
- cf[]数组传入m对相互冲突的节点编号。
- 返回问题是1否0有解。
- */
- int Two_SAT(int gp[][2],int n,int cf[][2],int m){
- int i,j,x,y,k,n2=n+n;
- Node *e[N],*es[N],*p;
- int opp[N],con[N],ncon; //强连通分量数组
- //get opp
- for(k=i=0;i<n;i++){ opp[gp[i][0]]=gp[i][1];opp[gp[i][1]]=gp[i][0]; }
- //create map e[]
- clr(e); memp=0;
- for(k=0;k<m;k++){
- i=cf[k][0]; j=cf[k][1];
- addEdge(e,i,opp[j]); addEdge(e,j,opp[i]);
- }
- //Shrink Connections
- ncon = Connection(e,n2,con);
- //judge
- for(i=0;i<n2;i++) if(con[i]==con[opp[i]]) return 0;
- return 1;
- }
- /****************************************/
- int ID(int i,int k){return i*2+k;}
- int c[3][2][2]={{{0,0},{0,1}},
- {{0,1},{1,1}},
- {{0,1},{1,0}}};
- int gp[N][2],cf[M][2],ret[N];
- int main()
- {
- int i,j,k;
- int n,m,cn,p,q,v,r;
- char s[10];
- while(scanf("%d%d",&n,&m)!=EOF){
- for(i=0;i<n;i++) for(k=0;k<2;k++)
- gp[i][k]=ID(i,k);
- //input
- cn=0;
- for(k=0;k<m;k++){
- scanf("%d%d%d%s",&i,&j,&v,s);
- if(s[0]=='A')r=0; if(s[0]=='O')r=1; if(s[0]=='X')r=2;
- for(p=0;p<2;p++) for(q=0;q<2;q++) if(c[r][p][q]!=v){
- cf[cn][0]=ID(i,p);
- cf[cn++][1]=ID(j,q);
- }
- }
- //judge
- if(Two_SAT(gp,n,cf,cn)) puts("YES");
- else puts("NO");
- }
- return 0;
- }