2-SAT 理解与模板

自我分析:

关于 2-SAT 的代码,有一些矛盾关系一定需要搞清楚;在求解中,用到了 tarjan 算法版本的强连通(SCC)算法;在输出结果时,采用拓扑排序,并同时染色。

POJ 2723 Get Luffy Out

POJ 3207 Ikkis Story IV Pandas Trick

POJ 3648- Wedding

POJ 3678 Katu Puzzle

POJ 3683 Priest Johns Busiest Day

POJ 2749  Building roads


tarjan算法:

dfn[u] 表示 u 结点的访问时间,low[u] 表示 u 以及 u 的后代能访问到的最早的最先结点 v 的 dfn[v]
如果 dfn[u] == low[v],则说明 u 是 DFS 树中 u 所在 SCC 的起始点,这时 u 之后栈内的结点都是 u 所在 SCC 中的结点,将它们出栈并染色
计算 low[u] 的方法和无向图求割顶割边时类似
(u, v) in E
若 v 未被访问,即 (u, v) 为树枝边,low[u] = min(low[u], low[v])
若 v 已被访问,如果 v 在栈中,(u, v) 可以为后向边也可以为连接同一个 DFS 树中没有后代关系的两个结点的交叉边,
        low[u] = min(low[u], dfn[v])
        如果 v 不在栈中,(u, v) 为连接不同 DFS 树的交叉边,v 处于另一个 SCC,不必考虑


构图(矛盾)思路:

(1)A and B = 0 添加弧  A->!B , B->!A

(2)A and B = 1         !A->A , !B->B
(3)A or B = 0          A->!A , B->!B
(4)A or B = 1          !A->B , !B->A
(5)A xor B = 0         A->B , B->A , !A->!B , !B->!A
// A == 1 && B == 0 : A -> B, !B -> !A; A == 0 && B == 1 : !A -> !B, B -> A;
(6)A xor B = 1         A->!B , B->!A , !B->A , !A->B

// 如果 x 一定在,则连一条 x + n -> x 的边(推导过程略)


const int maxn = 11000;
const int inf = 1000000000;
vector<int>edge[maxn];
int n, m;
int tmpdfn, dfn[maxn], low[maxn], inst[maxn], belong[maxn], st[maxn], top, scnt;
void tarjan( int u )
{
	int i, v, t, size;
	low[u] = dfn[u] = tmpdfn++;
	st[top++] = u;
	inst[u] = 1;
	size = edge[u].size();
	for( i = 0; i < size; i++ )
	{
		v = edge[u][i];
		if( dfn[v] == -1 )
		{
			tarjan( v );
			low[u] = min( low[u], low[v] );
		}
		else if( inst[v] )low[u] = min( low[u], dfn[v] );
	}
	if( dfn[u] == low[u] )
	{
		do{ belong[t = st[--top]] = scnt; inst[t] = 0; }while( t != u );
		scnt++;
	}
}
bool SCC()
{
	int i;
	top = 0;
	tmpdfn = scnt = 1;
	memset( dfn, -1, sizeof(dfn) );
	memset( inst, 0, sizeof(inst) );
	for( i = 1; i <= n * 2; i++ )if( dfn[i] == -1 )tarjan( i );
	for( i = 1; i <= n; i++ )if( belong[i] == belong[i + n] )return false;
	return true;
}

// 求解:建缩点后反向图,再拓扑排序(染色),同一颜色的一组即为解
int opp[maxn], in[maxn], color[maxn];
vector<int>arc[maxn];
queue<int>q;
void rebuild()
{
	memset( in, 0, sizeof(in) );
	int u, v, i, size;
	for( i = 1; i <= n; i++ )
	{
		opp[belong[i]] = belong[i + n];
		opp[belong[i + n]] = belong[i];
	}
	for( i = 1; i <= n * 2; i++ )arc[i].clear();
	for( u = 1; u <= n * 2; u++ )
	{
		size = edge[u].size();
		for( i = 0; i < size; i++ )
		{
			v = edge[u][i];
			if( belong[u] != belong[v] )
			{
				arc[belong[v]].push_back( belong[u] );
				in[belong[u]]++;
			}
		}
	}
}

void topusort()
{
	int i, j, u, v, size;
	while( !q.empty() )q.pop();
	memset( color, 0, sizeof(color) );
	for( i = 1; i < scnt; i++ )if( !in[i] )
		q.push( i );
	while( !q.empty() )
	{
		u = q.front();	q.pop();
		if( !color[u] )color[u] = 1, color[opp[u]] = 2;
		size = arc[u].size();
		for( i = 0; i < size; i++ )
		{	
			v = arc[u][i];
			in[v]--;
			if( !in[v] )q.push( v );
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值