淺談2-sat的問題的構造與求解

2-sat問題是一種常見的問題。給定若干個01變量,變量之間滿足一些二元約束,求是否有解存在。若存在,給出可行解或按照字典序給出最優解。

下面給出與其對應的圖論模型:給每個變量i設立2個點,我的習慣是記爲T(i),F(i),分別表示其值取1,0.

下面考慮的便是如何進行限制了。


一般的限制形式均如下所示:

變量i取x時,變量j只能取y,那麼表示i取x的點向表示j取y的點連一條有向邊。表示推出關係。

類似的,若表示變量i取x時,變量j不能取y,那麼表示i取x的點向表示j取~y的點連一條有向邊。因爲每個變量必定有值,且只有一個值。

以上這些限制每次都需要連兩條邊,即原命題和其逆否命題。

有一些另外的限制比較特殊:即變量i只能取x,那麼表示i取~x的點向表示i取x的點連一條有向邊.若表示變量i不能取x,那麼表示i取x的點向表示i取~x的點連一條有向邊.這些限制只連一條邊。


接下來,利用Tarjan算法求解強聯通分量,若表示某個變量爲0的點和表示這個變量爲1的點在同一個強聯通分量中,表示存在矛盾,2-sat問題無解。


下面看一下如何求出一組可行解。

我們構造求出的DAG的反圖,依照拓撲序依次進行處理。對於當前點,若沒有被染色,則染色爲1,並將這個連通分量中所有點的另一個解所對應的點所在的分量及其子孫均染色爲2.(注意,這是在反圖中。)染色就遞歸去染就好了,當遇到一個已經被染色爲2的點就不向下染色了。


那麼最終每個變量的解就是被染色爲1的分量所包含的該變量所對應的解。


下面來看幾道題:

POJ3207

Sol:以這條邊在圈內作爲0,在圈外作爲1,限制就是如果兩條邊區間相交,那麼值不能相同。那麼T(i)->F(j),T(j)->F(i).只需判斷這個2-sat問題是否有解即可。

Code:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <iostream>
#include <algorithm>
using namespace std;

#define N 510
int l[N], r[N];

int head[N << 1], next[N * N << 2], end[N * N << 2];
void addedge(int a, int b) {
	static int q = 1;
	end[q] = b;
	next[q] = head[a];
	head[a] = q++;
}

#define T(i) (i << 1)
#define F(i) ((i << 1) | 1)

int dfn[N << 1], low[N << 1], tclock, stack[N << 1], top, belong[N << 1], num;
bool instack[N << 1];

void dfs(int x) {
	dfn[x] = low[x] = ++tclock;
	stack[++top] = x;
	instack[x] = 1;
	for(int j = head[x]; j; j = next[j]) {
		if (!dfn[end[j]])
			dfs(end[j]), low[x] = min(low[x], low[end[j]]);
		else if (instack[end[j]])
			low[x] = min(low[x], dfn[end[j]]);
	}
	if (low[x] == dfn[x]) {
		++num;
		while(1) {
			int i = stack[top--];
			belong[i] = num;
			instack[i] = 0;
			if (i == x)
				break;
		}
	}
}

int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	
	register int i, j;
	int a, b;
	for(i = 1; i <= m; ++i) {
		scanf("%d%d", &a, &b);
		l[i] = ++a;
		r[i] = ++b;
		if (l[i] > r[i])
			swap(l[i], r[i]);
	}
	
	for(i = 1; i <= m; ++i) {
		for(j = i + 1; j <= m; ++j) {
			if (l[j] > l[i] && l[j] < r[i] && r[j] > r[i]) {
				addedge(T(i), F(j));
				addedge(F(i), T(j));
				addedge(T(j), F(i));
				addedge(F(j), T(i));
			}
		}
	}
	
	for(i = T(1); i <= F(m); ++i)
		if (!dfn[i])
			dfs(i);
			
	bool find = 0;
	for(i = 1; i <= m; ++i)
		if (belong[T(i)] == belong[F(i)]) {
			find = 1;
			break;
		}
	
	if (find)
		puts("the evil panda is lying again");
	else
		puts("panda is telling the truth...");
		
	return 0;
}

BZOJ1997

Sol:基本和上道題類似。也只需要判斷是否有解。

Code:

#include <cctype>
#include <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>
using namespace std;
 
inline int getc() {
    static const int L = 1 << 15;
    static char buf[L], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, L, stdin);
        if (S == T)
            return EOF;
    }
    return *S++;
}
inline int getint() {
    int c;
    while(!isdigit(c = getc()));
    int tmp = c - '0';
    while(isdigit(c = getc()))
        tmp = (tmp << 1) + (tmp << 3) + c - '0';
    return tmp;
}
 
#define N 210
#define M 10010
int hash[N];
 
struct Edge {
    int f, t;
    Edge(int _f = 0, int _t = 0):f(_f),t(_t){}
}E[M], sav[M];
int top;
 
struct Graph {
    int head[1210], next[819200], end[819200], ind;
    int dfn[1210], low[1210], tclock, stack[1210], top, belong[1210], cnt;
    bool instack[1210];
    void reset() {
        ind = tclock = top = cnt = 0;
        memset(head, -1, sizeof(head));
        memset(dfn, 0, sizeof(dfn));
    }
    void addedge(int a, int b) {
        int q = ind++;
        end[q] = b;
        next[q] = head[a];
        head[a] = q;
    }
    void dfs(int x) {
        dfn[x] = low[x] = ++tclock;
        instack[x] = 1;
        stack[++top] = x;
        for(int j = head[x]; j != -1; j = next[j]) {
            if (!dfn[end[j]]) {
                dfs(end[j]);
                low[x] = min(low[x], low[end[j]]);
            }
            else if (instack[end[j]])
                low[x] = min(low[x], dfn[end[j]]);
        }
        if (low[x] == dfn[x]) {
            ++cnt;
            while(1) {
                int i = stack[top--];
                belong[i] = cnt;
                instack[i] = 0;
                if (x == i)
                    break;
            }
        }
    }
}G;
 
#define T(i) (i * 2 - 2)
#define F(i) (i * 2 - 1)
 
inline bool is_intersect(int i, int j) {
    return sav[j].f > sav[i].f && sav[j].f < sav[i].t && sav[j].t > sav[i].t;
}
 
int main() {
    int T = getint();
     
    register int i, j;
    int n, m;
    while(T--) {
        n = getint(), m = getint();
        for(i = 1; i <= m; ++i)
            E[i].f = getint(), E[i].t = getint();
        int x;
        for(i = 1; i <= n; ++i) {
            x = getint();
            hash[x] = i;
        }
         
        if (m > 3 * n + 6) {
            puts("NO");
            continue;
        }
         
        top = 0;
        int f, t;
        for(i = 1; i <= m; ++i) {
            f = hash[E[i].f], t = hash[E[i].t];
            if (f > t)
                swap(f, t);
            if (!((f + 1 == t) || (f == 1 && t == n))) {
                sav[++top] = Edge(f, t);
            }
        }
         
        G.reset();
        for(i = 1; i <= top; ++i) {
            for(j = i + 1; j <= top; ++j) {
                if (is_intersect(i, j) || is_intersect(j, i)) {
                    G.addedge(T(i), F(j));
                    G.addedge(F(i), T(j));
                    G.addedge(T(j), F(i));
                    G.addedge(F(j), T(i));
                }
            }
        }
         
        for(i = T(1); i <= F(top); ++i)
            if (!G.dfn[i])
                G.dfs(i);
         
        bool find = 0;
        for(i = 1; i <= top; ++i)
            if (G.belong[T(i)] == G.belong[F(i)]) {
                find = 1;
                break;
            }
         
        if (find)
            puts("NO");
        else
            puts("YES");
    }
     
    return 0;
}

POJ3678

Sol:一堆亂七八糟的位運算。。。涉及到某變量強制取值的建模技巧。只需判斷是否有解。

Code:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <iostream>
#include <algorithm>
using namespace std;

#define N 1010
#define M 1000010
int head[N << 1], end[M << 2], next[M << 2];
void addedge(int a, int b) {
	static int q = 1;
	end[q] = b;
	next[q] = head[a];
	head[a] = q++;
}

#define T(i) (i << 1)
#define F(i) ((i << 1) | 1)

int dfn[N << 1], low[N << 1], tclock, stack[N << 1], top, belong[N << 1], num;
bool instack[N << 1];
void dfs(int x) {
	dfn[x] = low[x] = ++tclock;
	stack[++top] = x;
	instack[x] = 1;
	for(int j = head[x]; j; j = next[j]) {
		if (!dfn[end[j]]) {
			dfs(end[j]);
			low[x] = min(low[x], low[end[j]]);
		}
		else if (instack[end[j]])
			low[x] = min(low[x], dfn[end[j]]);
	}
	if (dfn[x] == low[x]) {
		++num;
		while(1) {
			int i = stack[top--];
			belong[i] = num;
			instack[i] = 0;
			if (i == x)
				break;
		}
	}
}

int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	
	register int i;
	char s[10];
	int a, b, x;
	
	for(i = 1; i <= m; ++i) {
		scanf("%d%d%d%s", &a, &b, &x, s);
		++a, ++b;
		if (s[0] == 'A') {
			if (x) {
				addedge(F(a), T(a));
				addedge(F(b), T(b));
			}
			else {
				addedge(T(a), F(b));
				addedge(T(b), F(a));
			}
		}
		if (s[0] == 'O') {
			if (x) {
				addedge(F(a), T(b));
				addedge(F(b), T(a));
			}
			else {
				addedge(T(a), F(a));
				addedge(T(b), F(b));
			}
		}
		if (s[0] == 'X') {
			if (x) {
				addedge(T(a), F(b));
				addedge(F(a), T(b));
				addedge(T(b), F(a));
				addedge(F(b), T(a));
			}
			else {
				addedge(T(a), T(b));
				addedge(F(a), F(b));
				addedge(T(b), T(a));
				addedge(F(b), F(a));
			}
		}
	}
	
	for(i = T(1); i <= F(n); ++i)
		if (!dfn[i])
			dfs(i);
	
	bool find = 0;
	for(i = 1; i <= n; ++i) {
		if (belong[T(i)] == belong[F(i)]) {
			find = 1;
			break;
		}
	}
	
	if (find)
		puts("NO");
	else
		puts("YES");
		
	return 0;
}

POJ3683

Sol:每個婚禮分爲是開頭還是結尾作爲01取值,建模很簡單,若區間相交則矛盾即可。

比較鍛鍊代碼能力的一道題。。。

Code:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cctype>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;

#define N 1010
int l[N], r[N], len[N];

int get(char *s) {
	return 600*(s[0]-'0')+60*(s[1]-'0')+10*(s[3]-'0')+(s[4]-'0');
}

int head[N << 1], next[N * N << 3], end[N * N << 3];
void addedge(int a, int b) {
	static int q = 1;
	end[q] = b;
	next[q] = head[a];
	head[a] = q++;
}

#define T(i) (i << 1)
#define F(i) ((i << 1) | 1)

int dfn[N << 1], low[N << 1], tclock, stack[N << 1], top, belong[N << 1], num;
vector<int> sav[N << 1];
bool instack[N << 1];
void dfs(int x) {
	dfn[x] = low[x] = ++tclock;
	stack[++top] = x;
	instack[x] = 1;
	for(int j = head[x]; j; j = next[j]) {
		if (!dfn[end[j]]) {
			dfs(end[j]);
			low[x] = min(low[x], low[end[j]]);
		}
		else if (instack[end[j]])
			low[x] = min(low[x], dfn[end[j]]);
	}
	if (dfn[x] == low[x]) {
		++num;
		while(1) {
			int i = stack[top--];
			instack[i] = 0;
			belong[i] = num;
			sav[num].push_back(i);
			if (x == i)
				break;
		}
	}
}

queue<int> q;
struct Graph {
	int head[N << 1], next[N * N << 3], end[N * N << 3], in[N << 1], ind, col[N << 1];
	void reset() {
		ind = 0;
		memset(head, -1, sizeof(head));
	}
	void addedge(int a, int b) {
		int q = ind++;
		end[q] = b;
		next[q] = head[a];
		head[a] = q;
		++in[b];
	}
	void Paint(int x) {
		col[x] = 2;
		for(int j = head[x]; j != -1; j = next[j])
			if (col[end[j]] != 2)
				Paint(end[j]);
	}
	void TopoSort() {
		int i, j;
		for(i = 1; i <= num; ++i)
			if (!in[i])
				q.push(i);
		while(!q.empty()) {
			i = q.front();
			q.pop();
			if (!col[i]) {
				col[i] = 1;
				int size = sav[i].size();
				for(j = 0; j < size; ++j)
					Paint(belong[sav[i][j] ^ 1]);
			}
			for(j = head[i]; j != -1; j = next[j])
				if (!--in[end[j]])
					q.push(end[j]);
		}
	}			
}G;

bool isnot_insect(int l1, int r1, int l2, int r2) {
	return r1 <= l2 || r2 <= l1;
}

void print(int x) {
	int t = x % 60;
	printf("%02d:%02d", (x - t) / 60, t);
}

int main() {
	int n;
	scanf("%d", &n);
	
	char s1[10], s2[10];
	int i, j;
	
	for(i = 1; i <= n; ++i) {
		scanf("%s%s%d", s1, s2, &len[i]);
		l[i] = get(s1);
		r[i] = get(s2);
	}
	
	for(i = 1; i <= n; ++i) {
		for(j = i + 1; j <= n; ++j) {
			if (!isnot_insect(l[i], l[i] + len[i], l[j], l[j] + len[j])) {
				addedge(T(i), F(j));
				addedge(T(j), F(i));
			}
			if (!isnot_insect(l[i], l[i] + len[i], r[j] - len[j], r[j])) {
				addedge(T(i), T(j));
				addedge(F(j), F(i));
			}
			if (!isnot_insect(r[i] - len[i], r[i], l[j], l[j] + len[j])) {
				addedge(F(i), F(j));
				addedge(T(j), T(i));
			}
			if (!isnot_insect(r[i] - len[i], r[i], r[j] - len[j], r[j])) {
				addedge(F(i), T(j));
				addedge(F(j), T(i));
			}
		}
	}
	
	for(i = T(1); i <= F(n); ++i)
		if (!dfn[i])
			dfs(i);
	
	bool find = 0;
	for(i = 1; i <= n; ++i)
		if (belong[T(i)] == belong[F(i)]) {
			find = 1;
			break;
		}
	
	if (find) {
		puts("NO");
		return 0;
	}
	
	G.reset();
	for(i = T(1); i <= F(n); ++i)
		for(j = head[i]; j; j = next[j])
			if (belong[i] != belong[end[j]])
				G.addedge(belong[end[j]], belong[i]);
	G.TopoSort();
	puts("YES");
	for(i = 1; i <= n; ++i) {
		if (G.col[belong[T(i)]] == 1)
			print(l[i]), putchar(' '), print(l[i] + len[i]);
		else
			print(r[i] - len[i]), putchar(' '), print(r[i]);
		puts("");
	}
	
	return 0;
}

BZOJ1823

Sol:十分的裸題。。。

Code:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <iostream>
#include <algorithm>
using namespace std;
 
#define N 110
#define M 1010
struct Graph {
    int head[N << 1], next[M << 1], end[M << 1], ind;
    int dfn[N << 1], low[N << 1], tclock, stack[N << 1], top, belong[N << 1], num;
    bool instack[N << 1];
    void reset() {
        ind = tclock = top = num = 0;
        memset(dfn, 0, sizeof(dfn));
        memset(head, -1, sizeof(head));
    }
    void addedge(int a, int b) {
        int q = ind++;
        end[q] = b;
        next[q] = head[a];
        head[a] = q;
    }
    void dfs(int x) {
        dfn[x] = low[x] = ++tclock;
        instack[x] = 1;
        stack[++top] = x;
        for(int j = head[x]; j != -1; j = next[j]) {
            if (!dfn[end[j]]) {
                dfs(end[j]);
                low[x] = min(low[x], low[end[j]]);
            }
            else if (instack[end[j]])
                low[x] = min(low[x], dfn[end[j]]);
        }
        if (dfn[x] == low[x]) {
            ++num;
            while(1) {
                int i = stack[top--];
                belong[i] = num;
                instack[i] = 0;
                if (x == i)
                    break;
            }
        }
    }
}G;
 
#define T(i) (i << 1)
#define F(i) ((i << 1) | 1)
 
int getint(char *s) {
    int len = strlen(s);
    int res = 0;
    for(int i = 1; i < len; ++i)
        res = (res << 3) + (res << 1) + s[i] - '0';
    return res;
}
 
int main() {
    int Case;
    scanf("%d", &Case);
     
    int n, m;
    char s1[60], s2[60];
    register int i, j;
    bool b1, b2;
    int n1, n2;
     
    while(Case--) {
        G.reset();
         
        scanf("%d%d", &n, &m);
        for(i = 1; i <= m; ++i) {
            scanf("%s%s", s1, s2);
            b1 = (s1[0] == 'm');
            b2 = (s2[0] == 'm');
            n1 = getint(s1);
            n2 = getint(s2);
            if (b1 && b2)
                G.addedge(F(n1), T(n2)), G.addedge(F(n2), T(n1));
            if (b1 && !b2)
                G.addedge(F(n1), F(n2)), G.addedge(T(n2), T(n1));
            if (!b1 && b2)
                G.addedge(T(n1), T(n2)), G.addedge(F(n2), F(n1));
            if (!b1 && !b2)
                G.addedge(T(n1), F(n2)), G.addedge(T(n2), F(n1));
        }
         
        for(i = T(1); i <= F(n); ++i)
            if (!G.dfn[i])
                G.dfs(i);
         
        bool find = 0;
        for(i = 1; i <= n; ++i)
            if (G.belong[T(i)] == G.belong[F(i)]) {
                find = 1;
                break;
            }
        if (find)
            puts("BAD");
        else
            puts("GOOD");
    }
     
    return 0;
}

就到這裏結束如何?
發佈了54 篇原創文章 · 獲贊 3 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章