POJ3678 Katu Puzzle
一道非常不错的建图2-SAT。
首先花了几分钟看懂题……毕竟是英文。然后我们发现其实一共只有六种限制,然后我们的目标就是求出一组布尔方程的解使得整个方程值为真。
我们进行一下转换:
1.a&&b=1,这样我们就必须保证a,b同时为真,那就从!a向a建边,!b向b建边。
2.a&&b=0,这样的话只要a,b不同时为真即可。这个一开始可能不知道该咋连(其实你要是瞎画也能画出来),不过后来发现这其实是a||b的相对情况,所以我们只要将a向!b连边,b向!a连边即可。
3.a || b = 1.这样只要a,b不同时为假即可,就是!a向b连边,!b向a连边。
4.a || b = 0,这样两者必须同时为假,与第一种情况相对,从a向!a建边,b向!b建边。
5.a^b = 1,也就是两者需要不同。我们把他转换成合取范式(这玩意我都忘了自己咋推出来的了),就是(a || b)&&(!a || !b)把两个子句转化一下建边。
6.a^b=0,两者相同。同样转换为合取范式(与上一种是相对应的),就是(!a || b) && (a || !b) ,把两个子句转化建边。
这样就建好啦,跑一遍2-SAT求解。
看一下代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define fill(x,y) memset(x,y,sizeof(x)); #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 10005; const int N = 500005; const int INF = 1000000009; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct edge { int next,from,to; }e[N<<2]; int n,m,a,b,c,ecnt,head[M],dfn[M],low[M],stack[M],top,idx,cnt,scc[M]; bool vis[M]; char s[10]; void add(int x,int y) { e[++ecnt].to = y; e[ecnt].from = x; e[ecnt].next = head[x]; head[x] = ecnt; } void tarjan(int x) { dfn[x] = low[x] =++idx; vis[x] = 1,stack[++top] = x; for(int i = head[x];i;i = e[i].next) { if(!dfn[e[i].to]) tarjan(e[i].to),low[x] = min(low[x],low[e[i].to]); else if(vis[e[i].to]) low[x] = min(low[x],dfn[e[i].to]); } if(dfn[x] == low[x]) { int p; cnt++; while((p = stack[top--])) { scc[p] = cnt,vis[p] = 0; if(x == p) break; } } } int main() { n = read(),m = read(); rep(i,1,m) { a = read(),b = read(),c = read(); a++,b++; scanf("%s",s); if(c == 1) { if(s[0] == 'A') add(a+n,a),add(b+n,b); else if(s[0] == 'O') add(a+n,b),add(b+n,a); else if(s[0] == 'X') add(a+n,b),add(a,b+n),add(b+n,a),add(b,a+n); } else if(c == 0) { if(s[0] == 'A') add(a,b+n),add(b,a+n); else if(s[0] == 'O') add(a,a+n),add(b,b+n); else if(s[0] == 'X') add(a,b),add(a+n,b+n),add(b+n,a+n),add(b,a); } } rep(i,1,n<<1) if(!dfn[i]) tarjan(i); rep(i,1,n) if(scc[i] == scc[i+n]) printf("NO\n"),exit(0); printf("YES\n"); return 0; }
当你意识到,每个上一秒都成为永恒。