1 /*HDU3062
2 2-sat入门
3 虽然是入门,关于2-sat算法的实质的理解还是花了蛮久的时间。
4 这道题还是有一些思维上的技巧。因为每对夫妇,最多且必须要去一个人,就是总共去了n个人。
5 我开始时以每个人分别去否建模,这样还要满足上面的条件,显然算法是难以实现的。
6 后来发现,只要以一对夫妻为对象,2i是丈夫去,2i+1是妻子去,非2i即2i+1,就像一件事情,非true即false
7 所以,不仅是一个对象有true,false两种状态,推广开来,只要是二选一(排他率)的二值问题,就可以向2-sat方向思考。
8 连边:因为2i和2i+1的相反关系,程序中已经能判断,所以不需考虑边的关系。
9 仇人关系,先转化成对应的点a,b。(非a且b)或(a和非b),即是a^1和b连边,a和b^1连边
10 判断是否是这个2-sat有解就可以了
11 */
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <math.h>
16 #include <ctype.h>
17 #include <string>
18 #include <iostream>
19 #include <sstream>
20 #include <vector>
21 #include <queue>
22 #include <stack>
23 #include <map>
24 #include <list>
25 #include <set>
26 #include <algorithm>
27 #define INF 0x3f3f3f3f
28 #define LL long long
29 #define eps 1e-7
30 #define maxn 1100
31 using namespace std;
32 struct TwoSAT{
33 int n;
34 vector<int> G[maxn*2];//注意点集的大小
35 bool mark[maxn*2];//联系《2-sat算法解析》中的红蓝标色,夫妻不能被标同一种颜色;因为仇人间没有连边,所以在图本身,不可能将两人标同一个颜色
36 int S[maxn*2],c;//存储当前被标记的点,可用于标记的回退
37
38 bool dfs(int x)
39 {
40 if (mark[x^1]) return false;//真假同时被标记,逻辑矛盾
41 if (mark[x]) return true;//x被标记,意味着下面的节点也被标记,思想是记忆化搜索
42 mark[x]=true;
43 S[c++]=x;
44 for(int i=0;i<G[x].size();i++)
45 if(!dfs(G[x][i])) return false;//同一个强联通分量应该表上同一种颜色
46 return true;
47 }
48
49 void init(int n)
50 {
51 this->n=n;
52 for(int i=0;i<n*2;i++) G[i].clear();
53 memset(mark,0,sizeof(mark));
54 }
55
56 void add_edge(int u,int v)//这个地方灵活多变一点
57 {
58 G[u].push_back(v);
59 // cout<<u<<"->"<<v<<endl;
60 }
61
62 bool solve()
63 {
64 for(int i=0;i<n*2;i+=2)
65 {
66 if(!mark[i] && !mark[i^1])//真假都没被标记才需dfs,思考一下,原书上写的是[mark+1],这是由i的取值和步长决定的,这里更改,使逻辑含义统一
67 {
68 c=0;//记得清零
69 if(!dfs(i))//将i标记为true
70 {
71 while(c>0) mark[S[--c]]=false;
72 if (!dfs(i^1)) return false;//更改初始标号颜色。只要有一个对象不能“二选一”,则2-sat无解
73 }
74 }
75 }
76 return true;
77 }
78 }sat;
79 //2i是丈夫去,2i+1是妻子去,
80 //仇人关系,先转化成对应的点a,b。(非a且b)或(a和非b),即是a^1和b连边,a和b^1连边,注意是双向边,满足对偶性
81 int n,m;
82 int nextint()
83 {
84 int x;
85 scanf("%d",&x);
86 return x;
87 }
88 int main()
89 {
90 while(cin>>n>>m)
91 {
92 // cout<<"Sdf"<<endl;
93 sat.init(n);//记住标号为相应的0--n-1
94 for(int i=1;i<=m;i++)
95 {
96 int a1,a2,c1,c2;
97 int a,b;
98 a1=nextint();
99 a2=nextint();
100 c1=nextint();
101 c2=nextint();
102 if(c1==0) a=a1*2+1;else a=a1*2;//0是妻子
103 if(c2==0) b=a2*2+1;else b=a2*2;
104 // sat.add_edge(a^1,b);
105 sat.add_edge(b,a^1);
106 sat.add_edge(a,b^1);
107 // sat.add_edge(b^1,a);
108 }
109 if (sat.solve()) cout<<"YES"<<endl;else cout<<"NO"<<endl;
110 }
111 }