hdu4115Eliminate the Conflict(2-SAT好题!)
题目描述:
Conflicts are everywhere in the world, from the young to the elderly, from families to countries. Conflicts cause quarrels, fights or even wars. How wonderful the world will be if all conflicts can be eliminated.
Edward contributes his lifetime to invent a 'Conflict Resolution Terminal' and he has finally succeeded. This magic item has the ability to eliminate all the conflicts. It works like this:
If any two people have conflict, they should simply put their hands into the 'Conflict Resolution Terminal' (which is simply a plastic tube). Then they play 'Rock, Paper and Scissors' in it. After they have decided what they will play, the tube should be opened and no one will have the chance to change. Finally, the winner have the right to rule and the loser should obey it. Conflict Eliminated!
But the game is not that fair, because people may be following some patterns when they play, and if the pattern is founded by others, the others will win definitely.
Alice and Bob always have conflicts with each other so they use the 'Conflict Resolution Terminal' a lot. Sadly for Bob, Alice found his pattern and can predict how Bob plays precisely. She is very kind that doesn't want to take advantage of that. So she tells Bob about it and they come up with a new way of eliminate the conflict:
They will play the 'Rock, Paper and Scissors' for N round. Bob will set up some restricts on Alice.
But the restrict can only be in the form of "you must play the same (or different) on the ith and jth rounds". If Alice loses in any round or break any of the rules she loses, otherwise she wins.
Will Alice have a chance to win?
题目大意:好长的题干,只过了四级的我瑟瑟发抖*~*,还好单词比较少....就是Alice和Bob两个人玩n轮石头剪刀布,Bob
的出牌(方便点)顺序已经告诉我们了,但是Alice的出牌有m个限制,a,b,c,如果c为1,就是第a轮和第b轮的出牌不能一样,
c为0表示a,b轮出牌必须一样,问咱们Alice是否能符合这些规则的情况下不输给Bob。
思路:最开始完全没思路,虽然知道是2-SAT的题目,但是建图和矛盾在哪里完全想不出来,Bob的出牌顺序咋用呢,感觉好困难。
犹豫了一会儿还是点开题解,看完题解思路后就做出来了,以下是我的理解。
石头剪刀布,Bob的出牌顺序已经告诉我们,如果Alice要不输的话每一轮就只有两种选择,我们可以预先用数组保存可能的选择情况
x*2表示第x轮的第一种选择,x*2+1表示第x轮的第二种可能。
矛盾(a,b,f)存在于不同轮之间,如果f=1,则a与b轮的出牌不能相同,而我们预先存取的可能情况中,a,b的四种组合(a1,b1),(a1,b2),(a2,b1),(a2,b2)中
是可能有相同这种情况的,假如(a1,b1)这种情况下,a1==b1,那么如果第a轮选择a1,则第b轮只能选b2,如果选择b1,则第a轮只能选择a2,
这就是矛盾所在,也就是2-SAT的原理,其余同理,然后f=0的情况就是如果(a1,b1)不相同,则选择a1就必须选择b2,选择b1就必须选择a2,
一样的道理。然后就能利用这些矛盾建立一个约束图,再缩点后,如果一个SCC中存在两个点来自同一轮,是一定不能满足的,这样咱们就能愉快的AC这道2-SAT中等题啦,
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 100100; struct edge { int f, t, nxt; }e[maxn]; int hd[maxn], tot; void add(int f, int t) { e[++tot] = { f,t,hd[f] }; hd[f] = tot; } int n, m; int low[maxn], dfn[maxn], cnt; int stk[maxn], sk, instk[maxn], col[maxn], colnum; void init() { memset(dfn, 0, sizeof(dfn)); memset(col, 0, sizeof(col)); memset(hd, 0, sizeof(hd)); tot = colnum = cnt = 0; } bool flg; void dfs(int u) {//缩点模板 low[u] = dfn[u] = ++cnt; stk[++sk] = u; instk[u] = 1; for (int i = hd[u]; i; i = e[i].nxt) { int v = e[i].t; if (!dfn[v]) { dfs(v); low[u] = min(low[u], low[v]); } else if (instk[v]) { low[u] = min(low[u], dfn[v]); } } if (low[u] == dfn[u]) { colnum++; while (1) { int v = stk[sk--]; if (col[v ^ 1] == colnum) { //如果相同位置的另一种可能和它会同时出现,即让Alice在某一轮同时出石头和剪刀,这是不可能成立的; flg = 0; } instk[v] = 0; col[v] = colnum; if (v == u)break; } } } int a[maxn]; int main() { //freopen("test.txt", "r", stdin); int t, cse = 1; scanf("%d", &t); while (t--) { scanf("%d%d", &n, &m); init(); for (int i = 0; i < n; i++) {//预先找出Alice可能的出牌情况 int s; scanf("%d", &s); /*s = s + 1 == 4 ? 1 : s + 1; a[i * 2] = s; s = s + 1 == 4 ? 1 : s + 1; a[i * 2 + 1] = s; //卧槽上面这样居然不会错,我之前是直接取1,2,3中除去s的两个点,但是石头剪刀布, //能不输的不该是s和克制s的那个数嘛,也就是下面这种,难道是这样矛盾也不会变?还是说hdu数据太水? //虽然这不是这题的重点hhh*/ if (s == 1) { a[i * 2] = 1; a[i * 2 + 1] = 2; } else if (s == 2) { a[i * 2] = 2; a[i * 2 + 1] = 3; } else { a[i * 2] = 3; a[i * 2 + 1] = 1; } } for (int i = 1; i <= m; i++) { int x, y, f; scanf("%d%d%d", &x, &y, &f); x--, y--; if (!f) {//要相等就找不相等的矛盾 int l = 2 * x, r = 2 * y; //异或运算就是该轮下的另一种选择,(0,1)(2,3); if (a[l] != a[r]) add(l, r ^ 1), add(r, l ^ 1); if (a[l] != a[r ^ 1])add(l, r), add(r ^ 1, l ^ 1); if (a[l ^ 1] != a[r])add(l ^ 1, r ^ 1), add(r, l); if (a[l ^ 1] != a[r ^ 1])add(l ^ 1, r), add(r ^ 1, l); } else {//同理 int l = 2 * x, r = 2 * y; if (a[l] == a[r]) add(l, r ^ 1), add(r, l ^ 1); if (a[l] == a[r ^ 1])add(l, r), add(r ^ 1, l ^ 1); if (a[l ^ 1] == a[r])add(l ^ 1, r ^ 1), add(r, l); if (a[l ^ 1] == a[r ^ 1])add(l ^ 1, r), add(r ^ 1, l); } } flg = 1; for (int i = 0; i < 2 * n; i++) { if (!dfn[i]) { dfs(i); } } printf("Case #%d: ", cse++); if (flg) { printf("yes\n"); } else { printf("no\n"); } } return 0; }