【2-SAT問題】解題報告:POJ 3678 Katu Puzzle(2-SAT問題的判定)

在這裏插入圖片描述
每個元素只有兩種可能的取值,所以是2-SAT的模型.
我們建立2*n個點,x∈[1…n]表示x取0,x∈[n+1…n+n]表示x取1
考慮將所給的關係轉化爲有向邊.

  • u and v=1:u,v都必須是1.爲了讓他們都是1,我們需要讓他們爲0時出現矛盾,也就是加邊(u,u+n),(v,v+n)
  • u and v=0:如果u=1那麼v=0,加邊(u+n,v).同理,加邊(v+n,u)
  • u or v=1:如果u=0那麼v=1加邊(u,v+n).同理,加邊(v,u+n)
  • u or v=0:u,v都必須是0.爲了讓他們都是0,我們需要讓他們爲1時出現矛盾,也就是加邊(u+n,u),(v+n,v)
  • u xor v=0:u=v,所以加邊(u,v),(v,u),(u+n,v+n),(v+n,u+n)
  • u xor v=1:u≠v,所以加邊(u,v+n),(v,u+n),(u+n,v),(v+n,u)

圖建完之後,跑一遍tarjan求出所有SCC,若出現矛盾(即存在scc[x]==scc[x+n],在同一個強連通分量中說明能互相到達,是一個環,也就是P則Q,那麼X既是1又是0所以有矛盾)就不存在解.否則存在.
時間複雜度O(n+m).注意邊數點數開足夠.

AC代碼:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<math.h>
#include<cstring>
#include<queue>
//#define ls (p<<1)
//#define rs (p<<1|1)
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
//#define lowbit(p) p&(-p)
using namespace std;

typedef long long ll;
typedef pair<int,int> PII;
const ll INF = 1e18;
const int N = 5e3;
const int M = 5e4+7;

int head[N],nex[M],ver[M],tot;
//vector<int>scc[N];
int cnt,n,m;
int dfn[N],low[N],num;
int stk[N],top;
int vis[N],c[N];
int in[N],out[N],ansi,anso;
int ins[N];

inline void read(int &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}

void add(int x,int y){
    ver[++tot] = y;nex[tot] = head[x];head[x] = tot;
}

void tarjan(int x){
    dfn[x] = low[x] = ++num;
    stk[++top] = x,ins[x] = 1;
    for(int i = head[x];i;i = nex[i]){
        int y = ver[i];
        if(!dfn[y]){
            tarjan(y);
            low[x] = min(low[x],low[y]);
        }
        else if(ins[y])
            low[x] = min(low[x],dfn[y]);
    }
    if(dfn[x] == low[x]){
        ++cnt;
        int y;
        do{
            y = stk[top--];
            ins[y] = 0;
            c[y] = cnt;
            //scc[cnt].push_back(y);
        }while(x != y);
    }
}

int main()
{
	cin>>n>>m;
	while(m--){
		int a,b,c;
		char s[7];
		scanf("%d %d %d %s",&a,&b,&c,s);
		if(s[0] == 'A'){
			if(c){
				//現在只是連起來,最後看是否在同一個強連通分量裏,在就說明是一個環 
				add(a,a+n);
				add(b,b+n);//雖然有連邊但是不一定是在一個強連通分量裏 
			}
			else {
				add(a+n,b);
				add(b+n,a);
			} 
		}else if(s[0] == 'O'){
			if(c){
				add(a,b+n);//至少有1個是1 
				add(b,a+n);
			}else {
				add(a+n,a);//應該都是0 
				add(b+n,b);
			} 
		} else {
			if(c){
				add(a,b+n);
				add(b,a+n);
				add(a+n,b);
				add(b+n,a);
			}else {
				add(a,b);
				add(b,a);
				add(a+n,b+n);
				add(b+n,a+n); 
			}
		}
	}
	for (int i = 1; i <= n; i++)
		if (!dfn[i]) tarjan(i);
	for (int i = 1; i <= n; i++)
		if (c[i] == c[i+n]) {
			puts("NO");
			return 0;
		}
	puts("YES");
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章