2-SAT總結

看到HDOJ上有一個2-SAT專題,刷完之後順便總結一下。

最簡單的2-SAT問題就是判斷是否有解,回答YES或NO就行了,不需要輸出具體的解。這種問題建圖完畢後一個SCC(強連通分量)就好了。如果要輸出解,就多一個拓撲排序。代碼中涉及多次建圖,注意細節就好了。

1.HDOJ 3062 Party

很裸的2-SAT模板,而且不用輸出路徑。
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define mp make_pair
#define MEMSET(a, b) memset(a, b, sizeof(a))
using namespace std;

typedef unsigned int ui;
typedef long long ll;
typedef unsigned long long ull;
typedef pair pii;
typedef vector vi;
typedef vi::iterator vi_it;
typedef map mii;
typedef priority_queue pqi;
typedef priority_queue, greater > rpqi;

const int MAX_N = 2000 + 10;
int link[MAX_N][MAX_N], rlink[MAX_N][MAX_N];
int stk[MAX_N];
bool vis[MAX_N];
int belong[MAX_N];
int top, cnt;

void dfs_visit(int (*lk)[MAX_N], int u, bool ft)
{
    vis[u] = true;
    for (int i = 1; i <= lk[u][0]; ++i) {
        if (!vis[lk[u][i]]) {
            dfs_visit(lk, lk[u][i], ft);
        }
    }
    if (ft) {
        stk[top++] = u;
    }
    else {
        belong[u] = cnt;
    }
}

void scc(int n)
{
    top = 0;
    MEMSET(vis, 0);
    for (int i = 0; i < n; ++i) {
        if (!vis[i]) {
            dfs_visit(link, i, true);
        }
    }
    cnt = 0;
    MEMSET(vis, 0);
    while (top) {
        if (!vis[stk[--top]]) {
            dfs_visit(rlink, stk[top], false);
            ++cnt;
        }
    }
}

bool two_sat(int n)
{
    scc(n << 1);
    for (int i = 0; i < n; ++i) {
        if (belong[i] == belong[i + n]) {
            return false;
        }
    }
    return true;
}

int main(int argc, char *argv[])
{
    int n, m, i, a1, a2, c1, c2;
    while (cin >> n >> m) {
        for (i = 0; i < (n << 1); ++i) {
            rlink[i][0] = link[i][0] = 0;
        }
        
        while (m--) {
            scanf("%d%d%d%d", &a1, &a2, &c1, &c2);
            int b1 = a1 + n * (c1 ^ 1);
            int b2 = a2 + n * (c2 ^ 1);
            a1 += n * c1;
            a2 += n * c2;
            link[a1][++link[a1][0]] = b2;
            link[a2][++link[a2][0]] = b1;
            rlink[b2][++rlink[b2][0]] = a1;
            rlink[b1][++rlink[b1][0]] = a2;
        }
        
        if (two_sat(n)) {
            puts("YES");
        }
        else {
            puts("NO");
        }
    }
    return 0;
}

這個題要求輸出一組解,另外需要根據區間的交來建圖,代碼比較長。判斷區間是否相交時我先排序,這樣內層循環不用遍歷所有j,不過效率其實差不多,只快了一點點。。。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define mp make_pair
#define X first
#define Y second
#define MEMSET(a, b) memset(a, b, sizeof(a))
using namespace std;

typedef unsigned int ui;
typedef long long ll;
typedef unsigned long long ull;
typedef pair pii;
typedef vector vi;
typedef vi::iterator vi_it;
typedef map mii;
typedef priority_queue pqi;
typedef priority_queue, greater > rpqi;

const int MAX_N = 2000 + 10;
int link[MAX_N][MAX_N], rlink[MAX_N][MAX_N];
int stk[MAX_N], ans[MAX_N], op[MAX_N];
bool vis[MAX_N], flag[MAX_N][MAX_N];
int belong[MAX_N];
int top, cnt;
pair cere[MAX_N];

void dfs_visit(int (*lk)[MAX_N], int u, bool ft)
{
    vis[u] = true;
    for (int i = 1; i <= lk[u][0]; ++i) {
        if (!vis[lk[u][i]]) {
            dfs_visit(lk, lk[u][i], ft);
        }
    }
    if (ft) {
        stk[top++] = u;
    }
    else {
        belong[u] = cnt;
    }
}

void scc(int n)
{
    top = 0;
    MEMSET(vis, 0);
    for (int i = 0; i < n; ++i) {
        if (!vis[i]) {
            dfs_visit(link, i, true);
        }
    }
    cnt = 0;
    MEMSET(vis, 0);
    while (top) {
        if (!vis[stk[--top]]) {
            dfs_visit(rlink, stk[top], false);
            ++cnt;
        }
    }
}

bool two_sat(int n)
{
    scc(n << 1);
    for (int i = 0; i < n + n; i += 2) {
        if (belong[i] == belong[i + 1]) {
            return false;
        }
    }
    return true;
}

void color(int u)
{
	ans[u] = 2;
	for (int i = 1; i <= rlink[u][0]; ++i) {
		if (!ans[rlink[u][i]]) {
			color(rlink[u][i]);
		}
	}
}

bool cmp(const pair &p1, const pair &p2)
{
	return p1.Y < p2.Y;
}

int main(int argc, char *argv[])
{
//	freopen("D:\\in.txt", "r", stdin);
	int n, i, j;
	cin >> n;
	for (i = 0; i < n; ++i) {
		int s1, s2, t1, t2, d;
		scanf("%d:%d %d:%d %d", &s1, &s2, &t1, &t2, &d);
		cere[i + i] = mp(mp(s1 * 60 + s2, s1 * 60 + s2 + d), i + i);
		cere[i + i + 1] = mp(mp(t1 * 60 + t2 - d, t1 * 60 + t2), i + i + 1);
	}
	
	sort(cere, cere + n + n);
	for (i = 0; i < n + n; ++i) {
		for (j = i + 1; j < n + n && cere[j].X.X < cere[i].X.Y; ++j) {			
			int u = cere[i].Y, v = cere[j].Y ^ 1;
			if (!flag[u][v]) {
				flag[u][v] = true;
				link[u][++link[u][0]] = v;
				rlink[v][++rlink[v][0]] = u;
			}
			u = cere[j].Y, v = cere[i].Y ^ 1;
			if (!flag[u][v]) {
				flag[u][v] = true;
				link[u][++link[u][0]] = v;
				rlink[v][++rlink[v][0]] = u;
			}
		}
	}
	
	if (!two_sat(n)) {
		puts("NO");
		return 0;
	}
	else {
		puts("YES");
	}
	
	MEMSET(flag, 0);
	for (i = 0; i < n + n; ++i) {
		rlink[i][0] = 0;
		op[belong[i]] = belong[i ^ 1];
	}
	for (i = 0; i < n + n; ++i) {
		int u = belong[i];
		for (j = 1; j <= link[i][0]; ++j) {
			int v = belong[link[i][j]];
			if (u != v && !flag[v][u]) {
				flag[v][u] = true;
				rlink[v][++rlink[v][0]] = u;
			}
		}
	}	

	MEMSET(vis, 0);
	top = 0;
	for (i = 0; i < cnt; ++i) {
		if (!vis[i]) {
			dfs_visit(rlink, i, true);
		}
	}
	
	while (top) {
		if (!ans[stk[--top]]) {
			ans[stk[top]] = 1;
			color(op[stk[top]]);
		}
	}
	
	sort(cere, cere + n + n, cmp);
	for (i = 0; i < n + n; ++i) {
		if (ans[belong[i]] == 1) {
			printf("%02d:%02d %02d:%02d\n", cere[i].X.X / 60, cere[i].X.X % 60,
				cere[i].X.Y / 60, cere[i].X.Y % 60);
		}
	}
	
	return 0;
}

和上題差不多的。
#include 
#include 
 
const int maxn=10010;
int n, m, nxt[maxn], head[maxn], pnt[maxn], ne, e, a, b, a0, b0;
char c1, c2;
int nnxt[maxn], nhead[maxn], npnt[maxn];
int order[maxn], norder, id[maxn], v[maxn];
int ans[maxn], op[maxn];
 
void dfs(int d){
    v[d] = 1;
    for(int i=head[d]; i!=-1; i=nxt[i])
        if(!v[pnt[i]])
            dfs(pnt[i]);
    order[norder++] = d;
}
void ndfs(int d, int k){
    v[d] = 1;
    id[d] = k;
    for(int i=nhead[d]; i!=-1; i=nnxt[i])
        if(!v[npnt[i]])
            ndfs(npnt[i], k);
}
void addedge(int s, int t){
    pnt[e] = t; nxt[e] = head[s]; head[s] = e++;
}
void addnedge(int t, int s){
    npnt[ne] = s; nnxt[ne] = nhead[t]; nhead[t] = ne++;
}
void color(int d){
    ans[d] = 2;
    for(int i=head[d]; i!=-1; i=nxt[i])
        if(!ans[pnt[i]])
            color(pnt[i]);
}
int main(){
    while(1){
        norder = e = ne = 0;
        memset(head, -1, sizeof head);
        memset(nhead, -1, sizeof nhead);
        scanf("%d%d", &n, &m);
        if(!n&&!m)
            break;
        for(int i=0; i=0; --i)
            if(!v[order[i]])
                ndfs(order[i], k++);
        int mark = 1;
        for(int i=0; i=0; --i)
            if(!ans[order[i]]){
                ans[order[i]] = 1;
                color(op[order[i]]);
            }
        for(int i=1; i

4.HDOJ 3715 Go Deeper

二分答案之後判斷2-SAT問題是否有解。SCC用tarjan寫的。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define mp make_pair
#define X first
#define Y second
#define MEMSET(a, b) memset(a, b, sizeof(a))
using namespace std;

typedef unsigned int ui;
typedef long long ll;
typedef unsigned long long ull;
typedef pair pii;
typedef vector vi;
typedef vi::iterator vi_it;
typedef map mii;
typedef priority_queue pqi;
typedef priority_queue, greater > rpqi;

const int MAX_N = 20000 + 10;
int head[MAX_N];
int edge[MAX_N * 100][2];
int dfn[MAX_N], low[MAX_N];
int stk[MAX_N];
int belong[MAX_N];
int top, cnt, idx, edgecnt;
int a[MAX_N], b[MAX_N], c[MAX_N];
int N;

void dfs(int u)
{
	dfn[u] = low[u] = ++idx;
	stk[top++] = u;
	int v;
	for (int i = head[u]; i != -1; i = edge[i][1]) {
		if (!dfn[v = edge[i][0]]) {
			dfs(v);
			low[u] = min(low[u], low[v]);
		}
		else if (!belong[v]) {
			low[u] = min(low[u], dfn[v]);
		}
	}
	
	if (dfn[u] == low[u]) {
		belong[u] = ++cnt;
		while (stk[--top] != u) {
			belong[stk[top]] = cnt;
		}
	}
}

void tarjan(int n)
{
	MEMSET(belong, 0);
	MEMSET(dfn, 0);
	cnt = idx = top = 0;
	for (int i = 1; i <= n; ++i) {
		if (!dfn[i]) {
			dfs(i);
		}
	}
}

bool two_sat(int n)
{
	tarjan(n + n);
	for (int i = 1; i <= n; ++i) {
		if (belong[i] == belong[i + n]) {
			return false;
		}
	}
	return true;
}

inline void add_edge(int u, int v)
{
	edge[edgecnt][0] = v;
	edge[edgecnt][1] = head[u];
	head[u] = edgecnt++;
}

void build(int n)
{
	int i;
	MEMSET(head, -1);
	edgecnt = 0;
	for (i = 1; i <= n; ++i) {
		if (c[i] == 0) {
			add_edge(a[i], b[i] + N);
			add_edge(b[i], a[i] + N);
		}
		else if (c[i] == 1) {
			add_edge(a[i], b[i]);
			add_edge(b[i], a[i]);
			add_edge(a[i] + N, b[i] + N);
			add_edge(b[i] + N, a[i] + N);
		}
		else {
			add_edge(a[i] + N, b[i]);
			add_edge(b[i] + N, a[i]);
		}
	}
}

int main(int argc, char *argv[])
{
//	freopen("D:\\in.txt", "r", stdin);
	int t, n, m, i, j;
	cin >> t;
	while (t--) {
		scanf("%d%d", &n, &m);
		N = n;
		for (i = 1; i <= m; ++i) {
			scanf("%d%d%d", a + i, b + i, c + i);
		}
		
		int low = 0, high = m, ans = 0;
		while (low <= high) {
			int mid = (low + high) >> 1;
			build(mid);
			if (two_sat(N)) {
				low = mid + 1;
				ans = mid;
			}
			else {
				high = mid - 1;
			}
		}
		printf("%d\n", ans);
	}
	
	return 0;
}

5.POJ 3207 Ikki's Story IV - Panda's Trick

也是只需要判斷,不過注意弦相交的判斷方法。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define mp make_pair
#define MEMSET(a, b) memset(a, b, sizeof(a))
using namespace std;

typedef unsigned int ui;
typedef long long ll;
typedef unsigned long long ull;
typedef pair pii;
typedef vector vi;
typedef vi::iterator vi_it;
typedef map mii;
typedef priority_queue pqi;
typedef priority_queue, greater > rpqi;

const int MAX_M = 1000 + 2;
int line[MAX_M][2];
vector link[MAX_M];
int dfn[MAX_M], low[MAX_M], belong[MAX_M];
int stk[MAX_M];
int cnt = 0, top = 0, idx = 0;

bool intersect(int *a, int *b, int n)
{
	if (a[0] < b[0] && b[0] < a[1] && a[1] < b[1]) {
		return true;
	}
	
	if (b[0] < a[0] && a[0] < b[1] && b[1] < a[1]) {
		return true;
	}
	
	return false;
}

void dfs(int u)
{
	dfn[u] = low[u] = ++idx;
	stk[top++] = u;
	int v;
	for (int i = 0; i < link[u].size(); ++i) {
		if (!dfn[v = link[u][i]]) {
			dfs(v);
			low[u] = min(low[u], low[v]);
		}
		else if (!belong[v]) {
			low[u] = min(low[u], dfn[v]);
		}
	}
	
	if (dfn[u] == low[u]) {
		belong[u] = ++cnt;
		while (stk[--top] != u) {
			belong[stk[top]] = cnt;
		}
	}
}

void tarjan(int n)
{
	for (int i = 0; i < n; ++i) {
		if (!dfn[i]) {
			dfs(i);
		}
	}
}

bool two_sat(int n)
{
	tarjan(n + n);
	for (int i = 0; i < n; ++i) {
		if (belong[i] == belong[i + n]) {
			return false;
		}
	}
	return true;
}

int main(int argc, char *argv[])
{
//	freopen("D:\\in.txt", "r", stdin);
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < m; ++i) {
		cin >> line[i][0] >> line[i][1];
		if (line[i][0] > line[i][1]) {
			swap(line[i][0], line[i][1]);
		}
		for (int j = 0; j < i; ++j) {
			if (intersect(line[i], line[j], n)) {
				link[i].push_back(j + m);
				link[j].push_back(i + m);
				link[i + m].push_back(j);
				link[j + m].push_back(i);
			}
		}
	}
	
	if (two_sat(m)) {
		puts("panda is telling the truth...");
	}
	else {
		puts("the evil panda is lying again");
	}
	
	return 0;
}

下面幾題還沒做,先給出鏈接




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