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('
')

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
"),exit(0);
    printf("YES
");
    return 0;
}
原文地址:https://www.cnblogs.com/captain1/p/9760902.html