关于2-sat,其实就是一些对于每个问题只有两种解,一般会给出问题间的关系,比如and,or,not等关系,判定是否存在解的问题。。
具体看http://blog.csdn.net/jarjingx/article/details/8521690,该博客写得很不错的。
下面是LRJ白书上给的2-sat,个人觉得写的很不错。。效率也很高。
1 #define maxn 500 2 struct TwoSat{ 3 int n; 4 vector<int> G[maxn * 2]; 5 bool mark[maxn * 2]; 6 int S[maxn * 2], c; 7 bool dfs(int x){ // 搜索一组解 8 if (mark[x^1]) return false; //出现冲突 9 if (mark[x]) return true; 10 mark[x] = true; 11 S[c++] = x; 12 for (int i = 0; i < G[x].size(); ++i) 13 if (!dfs(G[x][i])) return false; 14 return true; 15 } 16 17 void init(int n){ 18 this->n = n; 19 for (int i = 0; i < 2 * n; ++i) 20 G[i].clear(); 21 memset(mark, 0, sizeof(mark)); 22 } 23 24 void add_clause(int x, int xv, int y, int yv){ 25 x = x * 2 + xv; 26 y = y * 2 + yv; //x,y不能同时存在,那么如果选了y,合法解必定要选x^1 27 G[x].push_back(y^1); 28 G[y].push_back(x^1); 29 } 30 31 bool solve(){ 32 for (int i = 0; i < n * 2; i += 2) 33 if (!mark[i] && !mark[i+1]){ 34 c = 0; 35 if (!dfs(i)){ //枚举2种取值都无解 36 while (c > 0) mark[S[--c]] = false; 37 if (!dfs(i+1)) return false; 38 } 39 } 40 return true; 41 } 42 };
下面是poj上经典2-sat题目:
Poj2296:http://www.cnblogs.com/yzcstc/p/3588099.html
PoJ2723:
题意:
有m道门,每到门上有两把锁,打开其中的一把锁就能打开这道门,有2n把不同的钥匙,每把对应一种锁,这些钥匙被分成n对,每队钥匙只能取其中的一把,取了一把之后另一把就会消失,然后给出每一道门对应的钥匙,求解能打开的门的最大数量.注意,必须打开这一道门才能通往下一个门
思路:
二分一下答案,那么就转化为判定问题。接下去就是2-sat问题了
对于同一对钥匙a和b,属于互斥关系,添加a->~b, b->~a边
对于同一个门的两个钥匙,属于or的 关系,即~a与~b互斥,添加~a->b, ~b->a的边
Code:
1 /* 2 * Author: Yzcstc 3 * Created Time: 2014/3/8 17:14:52 4 * File Name: Poj2723.cpp 5 */ 6 #include<cstdio> 7 #include<iostream> 8 #include<cstring> 9 #include<cstdlib> 10 #include<cmath> 11 #include<algorithm> 12 #include<string> 13 #include<map> 14 #include<set> 15 #include<vector> 16 #include<queue> 17 #include<stack> 18 #include<ctime> 19 #define M0(x) memset(x, 0, sizeof(x)) 20 #define rep(i, a, b) for (int i = (a); i <= (b); ++i) 21 #define red(i, a, b) for (int i = (a); i >= (b); --i) 22 #define PB push_back 23 #define Inf 0x3fffffff 24 #define eps 1e-8 25 typedef long long LL; 26 using namespace std; 27 #define maxn 40000 28 int p[maxn]; 29 int X[maxn], Y[maxn], n, m, a[maxn], b[maxn]; 30 struct TwoSat{ 31 int n; 32 vector<int> G[maxn * 2]; 33 bool mark[maxn * 2]; 34 int S[maxn * 2], c; 35 bool dfs(int x){ // 搜索一组解 36 if (mark[x^1]) return false; //出现冲突 37 if (mark[x]) return true; 38 mark[x] = true; 39 S[c++] = x; 40 for (int i = 0; i < G[x].size(); ++i) 41 if (!dfs(G[x][i])) return false; 42 return true; 43 } 44 45 void init(int n){ 46 this->n = n; 47 for (int i = 0; i < 2 * n; ++i) 48 G[i].clear(); 49 memset(mark, 0, sizeof(mark)); 50 } 51 52 void add_clause(int x, int xv, int y, int yv){ 53 x = x * 2 + xv; 54 y = y * 2 + yv; //x,y不能同时存在,那么如果选了y,合法解必定要选x^1 55 G[x^1].push_back(y); 56 G[y^1].push_back(x); 57 } 58 59 bool solve(){ 60 for (int i = 0; i < n * 2; i += 2) 61 if (!mark[i] && !mark[i+1]){ 62 c = 0; 63 if (!dfs(i)){ //枚举2种取值都无解 64 while (c > 0) mark[S[--c]] = false; 65 if (!dfs(i+1)) return false; 66 } 67 } 68 return true; 69 } 70 } S; 71 72 void init(){ 73 int x, y; 74 for (int i = 0; i < n; ++i){ 75 scanf("%d%d", &x, &y); 76 X[i] = x; 77 Y[i] = y; 78 } 79 n *= 2; 80 for (int i = 1; i <= m; ++i) 81 scanf("%d%d", &a[i], &b[i]); 82 } 83 84 bool check(int floor){ 85 S.init(n); 86 for (int i = 0; i < n / 2; ++i){ 87 S.add_clause(X[i], 0, Y[i], 0); 88 // S.add_clause(Y[i], 1, X[i], 1); 89 } 90 for (int i = 1; i <= floor; ++i){ 91 S.add_clause(a[i], 1, b[i], 1); 92 // S.add_clause(b[i], 0, a[i], 0); 93 } 94 return S.solve(); 95 } 96 97 void solve(){ 98 int l = 1, r = m, ans = 0, mid; 99 while (l <= r){ 100 mid = (l + r) >> 1; 101 if (check(mid)){ans = mid; l = mid + 1; } 102 else r = mid - 1; 103 } 104 printf("%d ", ans); 105 } 106 107 int main(){ 108 // freopen("a.in", "r", stdin); 109 // freopen("a.out", "w", stdout); 110 while (scanf("%d%d", &n, &m) == 2 && n){ 111 init(); 112 solve(); 113 } 114 fclose(stdin); fclose(stdout); 115 return 0; 116 }
Poj2749
题意:
N 个牛栏,现在通过一条通道(s1,s2)把他们连起来,他们之间有一些约束关系,一些牛栏不能连在同一个点,一些牛栏必须连在同一个点,现在问有没有可能把他们都连好,而且满足所有的约束关系,如果可以,输出两个牛栏之间距离最大值的最小情况。
思路:
同样的二分答案。
至于建图:假定对于某个点a,连S1,为事件a,连S2,为事件~a
那么,对于不能连在一起的a, b两点,显然a与b,~a与~b互斥
另外一种情况则反过来。。
还有对于任意a与b,枚举他们的连接情况,一有矛盾同样建边。具体看代码吧
code:
1 /* 2 * Author: Yzcstc 3 * Created Time: 2014/3/12 14:56:50 4 * File Name: Poj2749.cpp 5 */ 6 #include<cstdio> 7 #include<iostream> 8 #include<cstring> 9 #include<cstdlib> 10 #include<cmath> 11 #include<algorithm> 12 #include<string> 13 #include<map> 14 #include<set> 15 #include<vector> 16 #include<queue> 17 #include<stack> 18 #include<ctime> 19 #define M0(x) memset(x, 0, sizeof(x)) 20 #define rep(i, a, b) for (int i = (a); i <= (b); ++i) 21 #define red(i, a, b) for (int i = (a); i >= (b); --i) 22 #define PB push_back 23 #define Inf 0x3fffffff 24 #define eps 1e-8 25 #define maxn 2000 26 typedef long long LL; 27 using namespace std; 28 struct TwoSat{ 29 int n; 30 bool mark[maxn]; 31 vector<int> G[maxn]; 32 int S[maxn], c; 33 bool dfs(int x){ 34 if (mark[x^1]) return false; 35 if (mark[x]) return true; 36 mark[x] = true; 37 S[c++] = x; 38 for (int i = 0; i < G[x].size(); ++i) 39 if (!dfs(G[x][i])) return false; 40 return true; 41 } 42 43 void init(int n){ 44 this->n = n; 45 for (int i = 0; i < 2 * n; ++i) G[i].clear(); 46 memset(mark, 0, sizeof(mark)); 47 } 48 49 void add_clause(int x, int xv, int y, int yv){ 50 x = x * 2 + xv; 51 y = y * 2 + yv; 52 G[x].push_back(y^1); 53 G[y].push_back(x^1); 54 } 55 56 bool solve(){ 57 for (int i = 0; i < n * 2; i += 2) 58 if (!mark[i] && !mark[i+1]){ 59 c = 0; 60 if (!dfs(i)){ 61 while (c > 0) mark[S[--c]] = false; 62 if (!dfs(i+1)) return false; 63 } 64 } 65 return true; 66 } 67 } S; 68 int X[1200], Y[1200], F[1200][2], H[1200][2], n, A, B, d[1000][2], dst; 69 int Sx1, Sx2, Sy1, Sy2; 70 71 void init(){ 72 scanf("%d%d%d%d", &Sx1, &Sy1, &Sx2, &Sy2); 73 for (int i = 0; i < n; ++i) 74 scanf("%d%d", &X[i], &Y[i]); 75 for (int i = 0; i < A; ++i) 76 scanf("%d%d", &H[i][0], &H[i][1]), --H[i][0], --H[i][1]; 77 for (int i = 0; i < B; ++i) 78 scanf("%d%d", &F[i][0], &F[i][1]), --F[i][0], --F[i][1]; 79 } 80 81 int dist(int x1, int y1, int x2, int y2){ 82 return abs(x1 - x2) + abs(y1 - y2); 83 } 84 85 void get_dist(){ 86 dst = dist(Sx1, Sy1, Sx2, Sy2); 87 for (int i = 0; i < n; ++i){ 88 d[i][0] = dist(X[i], Y[i], Sx1, Sy1); 89 d[i][1] = dist(X[i], Y[i], Sx2, Sy2); 90 } 91 } 92 93 bool check(int limit){ 94 S.init(n); 95 for (int i = 0; i < A; ++i){ 96 S.add_clause(H[i][0], 0, H[i][1], 0); 97 S.add_clause(H[i][0], 1, H[i][1], 1); 98 } 99 for (int i = 0; i < B; ++i){ 100 S.add_clause(F[i][0], 0, F[i][1], 1); 101 S.add_clause(F[i][0], 1, F[i][1], 0); 102 } 103 int flag = 0; 104 for (int i = 0; i < n; ++i) 105 for (int j = i+1; j < n; ++j){ 106 flag = 0; 107 if (d[i][0] + d[j][0] > limit) { S.add_clause(i, 0, j, 0); flag++; } 108 if (d[i][0] + d[j][1] + dst > limit) { S.add_clause(i, 0, j, 1); flag++; } 109 if (d[i][1] + d[j][0] + dst > limit) { S.add_clause(i, 1, j, 0); flag++; } 110 if (d[i][1] + d[j][1] > limit) { S.add_clause(i, 1, j, 1); flag++; } 111 if (flag == 4) return false; 112 } 113 return S.solve(); 114 } 115 116 void solve(){ 117 get_dist(); 118 int ans = -1; 119 int l = 0, r = 4000000, mid; 120 while (l <= r){ 121 mid = (l + r) >> 1; 122 if (check(mid)){ans = mid; r = mid - 1; } 123 else l = mid + 1; 124 } 125 cout << ans << endl; 126 } 127 128 int main(){ 129 // freopen("a.in", "r", stdin); 130 // freopen("a.out", "w", stdout); 131 while (scanf("%d%d%d", &n, &A, &B) == 3){ 132 init(); 133 solve(); 134 } 135 fclose(stdin); fclose(stdout); 136 return 0; 137 }
Poj3207:
题意:
一个圆上有n个点,依次从0到n-1,现在给出m条线段,每条连接两个点,线段可以在圆内也可以在圆外,问能否使得所有线段都不相交。
思路:
简单2-sat。对于两条相交的线段建边。。
code:
1 /* 2 * Author: Yzcstc 3 * Created Time: 2014/3/12 19:09:31 4 * File Name: poj3207.cpp 5 */ 6 #include<cstdio> 7 #include<iostream> 8 #include<cstring> 9 #include<cstdlib> 10 #include<cmath> 11 #include<algorithm> 12 #include<string> 13 #include<map> 14 #include<set> 15 #include<vector> 16 #include<queue> 17 #include<stack> 18 #include<ctime> 19 #define M0(x) memset(x, 0, sizeof(x)) 20 #define rep(i, a, b) for (int i = (a); i <= (b); ++i) 21 #define red(i, a, b) for (int i = (a); i >= (b); --i) 22 #define PB push_back 23 #define Inf 0x3fffffff 24 #define eps 1e-8 25 #define maxn 2014 26 typedef long long LL; 27 using namespace std; 28 struct TwoSat{ 29 int n; 30 int S[maxn], c; 31 bool mark[maxn]; 32 vector<int> G[maxn]; 33 bool dfs(int x){ 34 if (mark[x^1]) return false; 35 if (mark[x]) return true; 36 mark[x] = true; 37 S[c++] = x; 38 for (int i = 0; i < G[x].size(); ++i) 39 if (!dfs(G[x][i])) return false; 40 return true; 41 } 42 43 void init(int n){ 44 this->n = n; 45 for (int i = 0; i < n * 2; ++i) G[i].clear(); 46 memset(mark, 0, sizeof(mark)); 47 } 48 49 void add_clause(int x, int y){ 50 G[x].push_back(y^1); 51 G[y].push_back(x^1); 52 } 53 bool solve(){ 54 for (int i = 0; i < n * 2; i += 2) 55 if (!mark[i] && !mark[i+1]){ 56 c = 0; 57 if (!dfs(i)){ 58 while (c > 0) mark[S[--c]] = false; 59 if (!dfs(i+1)) return false; 60 } 61 } 62 return true; 63 } 64 } S; 65 int m, n, X[2014], Y[2014]; 66 67 bool In(int a, int b, int c){ 68 if (a < b && a < c && c < b) return true; 69 if (a > b && (c > a || c < b)) return true; 70 return false; 71 } 72 void solve(){ 73 for (int i = 0; i < n; ++i) 74 scanf("%d%d", &X[i], &Y[i]); 75 S.init(n); 76 for (int i = 0; i < n; ++i) 77 for (int j = i+1; j < n; ++j) 78 if (In(X[i], Y[i], X[j]) && In(Y[i], X[i], Y[j]) 79 || In(X[i], Y[i], Y[j]) && In(Y[i], X[i], X[j])){ 80 S.add_clause(i*2, j*2); 81 S.add_clause(i*2+1, j*2+1); 82 } 83 bool ans = S.solve(); 84 if (ans) puts("panda is telling the truth..."); 85 else puts("the evil panda is lying again"); 86 } 87 88 int main(){ 89 // freopen("a.in", "r", stdin); 90 // freopen("a.out", "w", stdout); 91 while (scanf("%d%d", &m, &n) == 2) solve(); 92 fclose(stdin); fclose(stdout); 93 return 0; 94 }
Poj3648:
题意:
有一对新人结婚,邀请n对夫妇去参加婚礼。有一张很长的桌子,人只能坐在桌子的两边,还要满足下面的要求:1.每对夫妇不能坐在同一侧 2.n对夫妇之中可能有通奸关系(包括男男,男女,女女),有通奸关系的不能同时坐在新娘的对面,可以分开坐,可以同时坐在新娘这一侧。如果存在一种可行的方案,输出与新娘同侧的人。
思路:
算是这几道题比较容易错的了 。
对于x与y通奸,则建x->~y, y->~x的边
而特殊的是新娘要与新郎连一条边,表示坐在对面
code:
1 /* 2 * Author: Yzcstc 3 * Created Time: 2014/3/12 20:08:13 4 * File Name: poj3468.cpp 5 */ 6 #include<cstdio> 7 #include<iostream> 8 #include<cstring> 9 #include<cstdlib> 10 #include<cmath> 11 #include<algorithm> 12 #include<string> 13 #include<map> 14 #include<set> 15 #include<vector> 16 #include<queue> 17 #include<stack> 18 #include<ctime> 19 #define M0(x) memset(x, 0, sizeof(x)) 20 #define rep(i, a, b) for (int i = (a); i <= (b); ++i) 21 #define red(i, a, b) for (int i = (a); i >= (b); --i) 22 #define PB push_back 23 #define Inf 0x3fffffff 24 #define eps 1e-8 25 #define maxn 50014 26 typedef long long LL; 27 using namespace std; 28 struct TwoSat{ 29 int n; 30 int S[maxn], c; 31 bool mark[maxn]; 32 vector<int> G[maxn]; 33 bool dfs(int x){ 34 if (mark[x^1]) return false; 35 if (mark[x]) return true; 36 mark[x] = true; 37 S[c++] = x; 38 for (int i = 0; i < (int)G[x].size(); ++i) 39 if (!dfs(G[x][i])) return false; 40 return true; 41 } 42 43 void init(int n){ 44 this->n = n; 45 for (int i = 0; i < n * 2; ++i) G[i].clear(); 46 memset(mark, 0, sizeof(mark)); 47 } 48 49 void add_clause(int x, int y){ 50 G[x^1].push_back(y); 51 G[y^1].push_back(x); 52 } 53 bool solve(){ 54 for (int i = 0; i < n * 2; i += 2) 55 if (!mark[i] && !mark[i+1]){ 56 c = 0; 57 if (!dfs(i)){ 58 while (c > 0) mark[S[--c]] = false; 59 if (!dfs(i+1)) return false; 60 } 61 } 62 return true; 63 } 64 } S; 65 int n, m; 66 67 void solve(){ 68 int x, y; 69 char c1, c2; 70 S.init(n); 71 for (int i = 0; i < m; ++i){ 72 scanf("%d%c%d%c", &x, &c1, &y, &c2); 73 x = x * 2; 74 y = y * 2; 75 if (c1 == 'h') ++x; 76 if (c2 == 'h') ++y; 77 S.add_clause(x, y); 78 } 79 S.G[1].push_back(0); 80 if (!S.solve()){ 81 puts("bad luck"); 82 return; 83 } 84 for (int i = 1; i < n; ++i){ 85 if (S.mark[2*i]) printf("%dw", i); 86 else printf("%dh", i); 87 if (i == n-1) puts(""); 88 printf(" "); 89 } 90 } 91 92 int main(){ 93 // freopen("a.in", "r", stdin); 94 // freopen("a.out", "w", stdout); 95 while (scanf("%d%d", &n, &m) == 2 && n){ 96 // init(); 97 solve(); 98 } 99 fclose(stdin); fclose(stdout); 100 return 0; 101 }
poj3678:
题意:
有一个有向图G(V,E),每条边e(a,b)上有一个位运算符op(AND, OR或XOR)和一个值c(0或1)。
问能不能在这个图上的每个点分配一个值X(0或1),使得每一条边e(a,b)满足 Xa op Xb = c
思路:
a and b ==1 , !a->a , !b -> b
a and b ==0 , a->!b , b->!a
a or b ==1 , !a->b , !b->a
a or b ==0 , a->!a , b->!b
a xor b ==1 , a->!b,!b->a,!a->b,b->!a
a xor b ==0 , a->b,b->a,!a->!b,!b->!a
至于为什对于a && b == 1 要建立本身到本身反的边,是要推出矛盾。。
code:
1 /* 2 * Author: Yzcstc 3 * Created Time: 2014/3/16 21:06:08 4 * File Name: poj3678.cpp 5 */ 6 #include<cstdio> 7 #include<iostream> 8 #include<cstring> 9 #include<cstdlib> 10 #include<cmath> 11 #include<algorithm> 12 #include<string> 13 #include<map> 14 #include<set> 15 #include<vector> 16 #include<queue> 17 #include<stack> 18 #include<ctime> 19 #define M0(x) memset(x, 0, sizeof(x)) 20 #define rep(i, a, b) for (int i = (a); i <= (b); ++i) 21 #define red(i, a, b) for (int i = (a); i >= (b); --i) 22 #define PB push_back 23 #define Inf 0x3fffffff 24 #define eps 1e-8 25 #define maxn 4200 26 typedef long long LL; 27 using namespace std; 28 struct TwoSat{ 29 int n; 30 vector<int> G[maxn]; 31 bool mark[maxn]; 32 int c, S[maxn]; 33 bool dfs(int x){ 34 if (mark[x^1]) return false; 35 if (mark[x]) return true; 36 mark[x] = true; 37 S[c++] = x; 38 for (int i = 0; i < G[x].size(); ++i) 39 if (!dfs(G[x][i])) return false; 40 return true; 41 } 42 43 void init(int n){ 44 this->n = n; 45 memset(mark, 0, sizeof(mark)); 46 for (int i = 0; i < 2*n; ++i) 47 G[i].clear(); 48 } 49 50 void add_clause(int x, int y){ G[x].push_back(y); } 51 52 bool solve(){ 53 for (int i = 0; i < 2*n; i += 2) 54 if (!mark[i] && !mark[i+1]){ 55 c = 0; 56 if (!dfs(i)){ 57 while (c > 0) mark[S[--c]] = false; 58 if (!dfs(i+1)) return false; 59 } 60 } 61 return true; 62 } 63 64 } S; 65 int n, m; 66 67 void solve(){ 68 char s[10]; 69 int a, b, c; 70 S.init(n); 71 for (int i = 0; i < m; ++i){ 72 scanf("%d%d%d%s", &a, &b, &c, &s); 73 if (s[0] == 'A'){ 74 if (c == 1) { S.add_clause(a * 2, a * 2 + 1); S.add_clause(b * 2, b * 2 + 1); } 75 if (c == 0) { S.add_clause(b * 2 + 1, a * 2); S.add_clause(a * 2 + 1, b * 2); } 76 } 77 if (s[0] == 'O'){ 78 if (c == 1) { S.add_clause(b * 2, a * 2 + 1); S.add_clause(a * 2, b * 2 + 1); } 79 if (c == 0) { S.add_clause(a * 2 + 1, a * 2); S.add_clause(b * 2 + 1, b * 2); } 80 } 81 if (s[0] == 'X'){ 82 if (c == 1) { S.add_clause(b * 2, a * 2 + 1); S.add_clause(a * 2, b * 2 + 1); 83 S.add_clause(b * 2 + 1, a * 2); S.add_clause(a * 2 + 1, b * 2); 84 } 85 if (c == 0) { S.add_clause(b * 2, a * 2); S.add_clause(a * 2, b * 2); 86 S.add_clause(b * 2 + 1, a * 2 + 1); S.add_clause(a * 2 + 1, b * 2 + 1); 87 } 88 } 89 } 90 if (S.solve()) puts("YES"); 91 else puts("NO"); 92 } 93 94 int main(){ 95 // freopen("a.in", "r", stdin); 96 // freopen("a.out", "w", stdout); 97 while (scanf("%d%d", &n, &m) == 2) solve(); 98 fclose(stdin); fclose(stdout); 99 return 0; 100 }
Poj3683:
题意:
有一个牧师要给好几对新婚夫妇准备婚礼.,已知每对新婚夫妇的有空的时间以及婚礼持续时间..
问是否可以让每对新婚夫妇都得到该牧师的祝福。如果可以就输出YES以及可行解 不可以就输出NO
思路:
典型的2-sat。只不过多了输出解。
code:
1 /* 2 * Author: Yzcstc 3 * Created Time: 2014/3/18 18:06:09 4 * File Name: poj3683.cpp 5 */ 6 #include<cstdio> 7 #include<iostream> 8 #include<cstring> 9 #include<cstdlib> 10 #include<cmath> 11 #include<algorithm> 12 #include<string> 13 #include<map> 14 #include<set> 15 #include<vector> 16 #include<queue> 17 #include<stack> 18 #include<ctime> 19 #define M0(x) memset(x, 0, sizeof(x)) 20 #define rep(i, a, b) for (int i = (a); i <= (b); ++i) 21 #define red(i, a, b) for (int i = (a); i >= (b); --i) 22 #define PB push_back 23 #define Inf 0x3fffffff 24 #define maxn 8000 25 #define eps 1e-8 26 typedef long long LL; 27 using namespace std; 28 struct TwoSat{ 29 int n; 30 vector<int> G[maxn]; 31 int c, S[maxn]; 32 bool mark[maxn]; 33 bool dfs(int x){ 34 if (mark[x^1]) return false; 35 if (mark[x]) return true; 36 mark[x] = true; 37 S[c++] = x; 38 for (int i = 0; i < G[x].size(); ++i) 39 if (!dfs(G[x][i])) return false; 40 return true; 41 } 42 43 void init(int n){ 44 this->n = n; 45 memset(mark, 0, sizeof(mark)); 46 for (int i = 0; i < 2*n; ++i) 47 G[i].clear(); 48 } 49 void add_clause(int x, int y){ 50 G[x^1].push_back(y); 51 G[y^1].push_back(x); 52 } 53 bool solve(){ 54 for (int i = 0; i < 2*n; i += 2) 55 if (!mark[i] && !mark[i+1]){ 56 c = 0; 57 if (!dfs(i)){ 58 while (c > 0) mark[S[--c]] = false; 59 if (!dfs(i+1)) return false; 60 } 61 } 62 return true; 63 } 64 } S; 65 int n; 66 int St[2048], En[2048], D[2048]; 67 bool g[2048][2048]; 68 69 void init(){ 70 int x, y, x2, y2, z; 71 for (int i = 0; i < n; ++i){ 72 scanf("%d:%d %d:%d %d", &x, &y, &x2, &y2, &z); 73 D[i] = z; 74 St[i] = x * 60 + y; 75 En[i] = x2 * 60 + y2; 76 } 77 } 78 79 bool check(int S1, int T1, int S2, int T2){ 80 if (S1 < T2 && S2 < T1) return true; 81 return false; 82 } 83 84 void solve(){ 85 S.init(n); 86 int S1, T1, S2, T2; 87 M0(g); 88 for (int i = 0; i < n; ++i) 89 for (int j = i + 1; j < n; ++j){ 90 S1 = St[i], T1 = St[i] + D[i]; 91 S2 = St[j], T2 = St[j] + D[j]; 92 if (check(S1, T1, S2, T2)){ g[i*2][j * 2] = true; S.add_clause(i * 2, j * 2); } 93 S1 = St[i], T1 = St[i] + D[i]; 94 S2 = En[j] - D[j], T2 = En[j]; 95 if (check(S1, T1, S2, T2)) { g[i*2][j*2+1] = true; S.add_clause(i * 2, j * 2 + 1);} 96 S1 = En[i] - D[i], T1 = En[i]; 97 S2 = St[j], T2 = St[j] + D[j]; 98 if (check(S1, T1, S2, T2)) { g[i*2+1][j*2] = true; S.add_clause(i * 2 + 1, j * 2);} 99 S1 = En[i] - D[i], T1 = En[i]; 100 S2 = En[j] - D[j], T2 = En[j]; 101 if (check(S1, T1, S2, T2)) { g[i*2+1][j*2+1] = true; S.add_clause(i * 2 + 1, j * 2 + 1);} 102 } 103 if (!S.solve()){puts("NO"); return;} 104 puts("YES"); 105 vector<int> V; 106 for (int i = 0; i < n; ++i) 107 if (S.mark[i*2]) V.PB(i*2); 108 else V.PB(i*2+1); 109 bool flag = true; 110 for (int i = 0; i < V.size(); ++i) 111 for (int j = i+1; j < V.size(); ++j) 112 if (g[V[i]][V[j]] || g[V[j]][V[i]]) flag = false; 113 for (int i = 0; i < n; ++i){ 114 if (S.mark[i*2] == flag){ 115 printf("%02d:%02d %02d:%02d ", St[i] / 60, St[i] % 60, (St[i] + D[i]) / 60, (St[i] + D[i]) % 60); 116 continue; 117 } 118 printf("%02d:%02d %02d:%02d ", (En[i] - D[i]) / 60, (En[i] - D[i]) % 60, En[i] / 60, En[i] % 60); 119 } 120 } 121 122 int main(){ 123 // freopen("a.in", "r", stdin); 124 // freopen("a.out", "w", stdout); 125 while (scanf("%d", &n) != EOF){ 126 init(); 127 solve(); 128 } 129 fclose(stdin); fclose(stdout); 130 return 0; 131 }
Poj3905
题意:
有n个候选人,m组要求,每组要求关系到候选人中的两个人,“+i +j”代表i和j中至少有一人被选中,“-i -j”代表i和j中至少有一人不被选中。“+i -j”代表i被选中和j不被选中这两个事件至少发生一个,“-i +j”代表i不被选中和j被选中这两个事件至少发生一个。问是否存在符合所有m项要求的方案存在。
思路:
简单的2-sat
code:
1 /* 2 * Author: Yzcstc 3 * Created Time: 2014/3/19 0:06:34 4 * File Name: poj3905.cpp 5 */ 6 #include<cstdio> 7 #include<iostream> 8 #include<cstring> 9 #include<cstdlib> 10 #include<cmath> 11 #include<algorithm> 12 #include<string> 13 #include<map> 14 #include<set> 15 #include<vector> 16 #include<queue> 17 #include<stack> 18 #include<ctime> 19 #define M0(x) memset(x, 0, sizeof(x)) 20 #define rep(i, a, b) for (int i = (a); i <= (b); ++i) 21 #define red(i, a, b) for (int i = (a); i >= (b); --i) 22 #define PB push_back 23 #define Inf 0x3fffffff 24 #define eps 1e-8 25 #define maxn 4000 26 typedef long long LL; 27 using namespace std; 28 struct Twosat{ 29 int n; 30 int S[maxn], c; 31 vector<int> G[maxn]; 32 bool mark[maxn]; 33 bool dfs(int x){ 34 if (mark[x^1]) return false; 35 if (mark[x]) return true; 36 mark[x] = true; 37 S[c++] = x; 38 for (int i = 0; i < G[x].size(); ++i) 39 if (!dfs(G[x][i])) return false; 40 return true; 41 } 42 void init(int n){ 43 this->n = n; 44 memset(mark, 0, sizeof(mark)); 45 for (int i = 0; i < 2 * n; ++i) 46 G[i].clear(); 47 } 48 void add_clause(int x, int y){ 49 G[x^1].PB(y); 50 G[y^1].PB(x); 51 } 52 bool solve(){ 53 for (int i = 0; i < 2*n; i += 2) 54 if (!mark[i] && !mark[i+1]){ 55 c = 0; 56 if (!dfs(i)){ 57 while (c > 0) mark[S[--c]] = false; 58 if (!dfs(i+1)) return false; 59 } 60 } 61 return true; 62 } 63 } S; 64 int n, m; 65 66 int change(char s[], int n){ 67 int ret = 0; 68 for (int i = 0; i < n; ++i) 69 if(s[i] <= '9' && s[i] >= '0') ret = ret * 10 + s[i] - 48; 70 return ret; 71 } 72 73 void solve(){ 74 int x, y; 75 char s[20], s1[20]; 76 S.init(n); 77 for (int i = 0; i < m; ++i){ 78 scanf("%s%s", &s, &s1); 79 x = change(s, strlen(s)) - 1; 80 y = change(s1, strlen(s1)) - 1; 81 if (s[0] == '+' && s1[0] == '+') S.add_clause(x * 2 + 1, y * 2 + 1); 82 if (s[0] == '+' && s1[0] == '-') S.add_clause(x * 2 + 1, y * 2); 83 if (s[0] == '-' && s1[0] == '+') S.add_clause(x * 2, y * 2 + 1); 84 if (s[0] == '-' && s1[0] == '-') S.add_clause(x * 2, y * 2); 85 } 86 if (S.solve()) puts("1"); 87 else puts("0"); 88 } 89 90 int main(){ 91 // freopen("a.in", "r", stdin); 92 // freopen("a.out", "w", stdout); 93 while (scanf("%d%d", &n, &m) == 2) solve(); 94 fclose(stdin); fclose(stdout); 95 return 0; 96 }