UVA11294 Wedding

本文介绍了一种解决2-SAT问题的方法,并提供了两种实现方案的代码示例。通过将问题转化为图论中的二分图匹配问题,利用Tarjan算法进行强连通分量分解,再借助拓扑排序或深度优先搜索来寻找可行解。

微微发亮的传送门

2-SAT问题输出可行解

可以把一对夫妇当成一个节点,然后拆点的话,h和w分别为真和假,然后直接按照题目中说的建图染色即可

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
const int maxn = 222;
int pre[maxn], sccno[maxn], lowlink[maxn], scc_cnt, dfs_clock, ha[maxn];
int col[maxn], ind[maxn];
vector<int> G[maxn], g[maxn];
stack<int> S;
queue<int> que;
void add_clause(int x, int xval, int y, int yval){
	x = x * 2 + xval;
	y = y * 2 + yval;
	G[x ^ 1].push_back(y);
	G[y ^ 1].push_back(x);
}
void dfs(int u){  
    pre[u] = lowlink[u] = ++dfs_clock;  
    S.push(u);  
    for (int i = 0; i < G[u].size(); i++){  
        int v = G[u][i];  
        if (!pre[v]){  
            dfs(v);  
            lowlink[u] = min(lowlink[u], lowlink[v]);  
        }  
        else if (!sccno[v])  
            lowlink[u] = min(lowlink[u], pre[v]);  
    }  
    if (lowlink[u] == pre[u]){  
        scc_cnt += 1;  
        for (;;){  
            int x = S.top(); S.pop();  
            sccno[x] = scc_cnt;  
            if (x == u) break;  
        }  
    }  
}  
void tarjan(int n){  
    dfs_clock = scc_cnt = 0;  
    memset(pre, 0, sizeof(pre));  
    memset(sccno, 0, sizeof(sccno));  
    memset(lowlink, 0, sizeof(lowlink));  
    for (int i = 0; i < n; i++)  
        if (!pre[i]) dfs(i);  
}
int n, m, x, y, a, b;
char c1, c2;
void topsort(){
	 while(!que.empty())  {  
        int u = que.front();  
        que.pop();  
        int size = g[u].size();  
        if(!col[u]) col[u] = 1, col[ha[u]] = 2;  
        for(int i = 0; i < size; i++){  
            int v = g[u][i];  
            ind[v]--;  
            if(ind[v] == 0) que.push(v);  
        }  
    }  
}
int main(){
	while(~scanf("%d%d", &n, &m)){
		if (!n && !m) break;
		for (int i = 0; i <= n * 2; i++){
			G[i].clear(); g[i].clear();
		}
		for (int i = 0; i < m; i++){
			scanf("%d%c %d%c", &x, &c1, &y, &c2);
			a = c1 == 'h' ? 0 : 1;
			b = c2 == 'h' ? 0 : 1;
			add_clause(x, a, y, b);
		}
		G[0].push_back(1);
		tarjan(2 * n);
		memset(ha, 0, sizeof(ha));
		bool f = 1;
		for (int i = 0; i < n; i++)
			if (sccno[2 * i] == sccno[2 * i + 1]){
				f = 0; break;
			}
			else ha[sccno[2 * i]] = sccno[2 * i + 1], ha[sccno[2 * i + 1]] = sccno[2 * i];
		if (!f){
			printf("bad luck\n"); continue;
		}
		memset(ind, 0, sizeof(ind));
		for (int i = 0; i < 2 * n; i++)
			for (int j = 0; j < G[i].size(); j++){
				int v = G[i][j];
				if (sccno[i] != sccno[v]){
					g[sccno[v]].push_back(sccno[i]); ind[sccno[i]] += 1;
				}
			}
		while (!que.empty()) que.pop();
		for (int i = 1; i <= scc_cnt; i++)
			if (ind[i] == 0) que.push(i);
		memset(col, 0, sizeof(col));
		topsort();
		for (int i = 2; i < 2 * n; i += 2){
			if (i != 2) printf(" ");
			if (col[sccno[i]] == col[sccno[0]]) printf("%dw", i / 2);
			else printf("%dh", i / 2);
		}
		printf("\n");
	}
	return 0;
}

另附一佳哥版本的解法,速度竟然比一般解法快诶……佳哥不愧把算法当艺术的大牛……

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 222;
struct TowSAT{
	vector<int> G[maxn];
	bool mark[maxn];
	int S[maxn], c, n;
	bool dfs(int x){
		if (mark[x ^ 1]) return 0;
		if (mark[x]) return 1;
		mark[x] = 1;
		S[c++] = x;
		for (int i = 0; i < G[x].size(); i++)
			if (!dfs(G[x][i])) return 0;
		return 1;
	}
	void init(int n){
		this -> n = n;
		for (int i = 0; i <= n; i++)
			G[i].clear();
		memset(mark, 0, sizeof(mark));
	}
	void add_clause(int x, int xval, int y, int yval){
		x = x * 2 + xval;
		y = y * 2 + yval;
		G[x ^ 1].push_back(y);
		G[y ^ 1].push_back(x);
	}
	bool solve(){
		for (int i = 0; i < n * 2; i += 2)
			if (!mark[i] && !mark[i + 1]){
				c = 0;
				if (!dfs(i)){
					while(c > 0) mark[S[--c]] = 0;
					if (!dfs(i + 1)) return 0;
				}
			}
		return 1;
	}
};
int n, m, x, y, a, b;
char c1, c2;
TowSAT solver;
int main(){
	while(~scanf("%d%d", &n, &m)){
		if (!n && !m) break;
		solver.init(2 * n);
		for (int i = 0; i < m; i++){
			scanf("%d%c %d%c", &x, &c1, &y, &c2);
			a = c1 == 'h' ? 0 : 1;
			b = c2 == 'h' ? 0 : 1;
			solver.add_clause(x, a, y, b);
		}
		solver.G[0].push_back(1);
		if (!solver.solve()) printf("bad luck");
		else for (int i = 2; i < 2 * n; i += 2){
			if (i != 2) printf(" ");
			if (solver.mark[i] == solver.mark[0]) printf("%dw", i / 2);
			else printf("%dh", i / 2);
		}
		printf("\n");
	}
	return 0;
}


评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值