2-sat问题

       最近做了2-sat问题,基本没明白2-sat是个什么玩意,不过解题思路明确了,至于为什么……依旧是混沌迷茫中……

       网上公认的两篇大牛的文章,在此膜拜……

                    赵爽 2-sat解法浅析

伍昱 由对称性解2-SAT问题

       2-sat问题:我个人理解就是A有两个互斥的选择A和~A,A同时又和其他一些时间不能同时发生,然后在所给时间中找一个可行的方案,即解。

       解2-sat问题的方法两篇大牛的文章里已经有完整的证明,我也不会……

       一、建图

               根据题意找出题目中出现或隐含的互斥关系,然后根据不同时存在的条件构图。即找出原题中的A和~A都代表着什么,A和哪些节点不同时存在。

      假设A不与B同时存在,可B总要在B与~B中选择一个作为结果,所以A一定与~B在一个解中,将A和~B连接。当所有的边都找到并存好后,建图阶段

      以完成。(PS:是建一条边还是两条边呢?)

      二、强连通分量

              求出图中的强连通分量(哎,我对名词不敏感,不知道什么是强连通分量,百度后个人认为是一种类似环的东东)。一个强连通分量上的点都是

      在一个解里面的,所以如果A与~A都在一个圈里(不到专有名词是什么,这里是A能到~A,~A也能到A,是回路)。这种情况是无解,不用继续算了。

      若有解,就把原来的图缩点,成为一个有向无环图(没有圈了)。再进一步求解。

      三、拓扑排序

              用拓扑排序求出缩点后图的逆图的节点顺序(可能说的不对,反正我就是把原来的图拓扑排序,也可以把边都反向了在拓扑排序)。拓扑的过程

      很简单,做一个节点入度的数组,对数组进行遍历。每次拿出入度为零的点,删掉这个点所有的边(就是把到达的节点入度都减一)。

      四、染色

              按照拓扑排序的顺序进行染色(缩点的图就是拓扑的逆序,逆图就是顺序),每次找一个没有颜色的节点染成红色,并把它的互斥节点、及其子

      节点染成蓝色。

      五、输出答案

              根据染色结果选择答案进行输出。

PS:个人思路,个人想法,可能不对……自己检查…

..............................................................................................................................华丽的分割线..........................................................................................................................

例题 :poj 3648

题意:有好多对情侣,不过中间有通奸的,要求妻子对面坐的人没有通奸,或者成对夫妇。

/
// File Name: poj3648.cpp
// Author: Eggache
// mail: aszmq@163.com
// Created Time: 2013-8-12 15:10:07
/
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <cstring>
#include <cmath>

#include <algorithm>
#include <iostream>
#include <stack>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
#define mid(x,y) (x+y)>>1
#define INF (INT_MAX/10)
#define SQR(x) ((x)*(x))
#define rep(i, n) for (int i=0; i<(n); ++i)
#define repf(i, a, b) for (int i=(a); i<=(b); ++i)
#define repd(i, a, b) for (int i=(a); i>=(b); --i)
#define clr(ar,val) memset(ar, val, sizeof(ar))
#define N 100000
struct Edge{
	int v,to;
}edge[N+10],nedge[N+10];
int edgeNum,head[N+10];
void add(int u,int v){
	edge[edgeNum].v=v;                       //edge[]   edgeNum
	edge[edgeNum].to=head[u];                //head[]
	head[u]=edgeNum++;
}                               //构造原图的邻接表
int dfn[N+10],low[N+10],id[N+10],ans[N+10],s[N+10];
int cnt,scnt,beg;
void dfs(int x){
	low[x]=dfn[x]=++cnt;                     //low[]   dfn[]   cnt
	s[++beg]=x;                              //s[]     beg
	int v;
	for(int i=head[x];i!=-1;i=edge[i].to){
		v=edge[i].v;
		if(!dfn[v]){
			dfs(v);
			low[x]=min(low[v],low[x]);
		}
		else if(!id[v])                      //id[]   
			low[x]=min(dfn[v],low[x]);
	}
	if(dfn[x]==low[x]){
		int tmp=0;
		scnt++;                              //scnt
		do{
			tmp++;
			v=s[beg--];
			id[v]=scnt;
		}while(v!=x);
		ans[scnt]=tmp;                       //ans[]
	}
}
void tarjan(int n){
	cnt=beg=scnt=0;
	clr(dfn,0);
	rep(i, n) if(!dfn[i]) dfs(i);
}                                 //求强连通分量tarjan算法
int nhead[N+10];
void nadd(int u,int v){
	nedge[edgeNum].v=v;                     //nedge[]
	nedge[edgeNum].to=nhead[u];             //nhead[]
	nhead[u]=edgeNum++;
}                                 //构造反向图的邻接表
int op[N+10],in[N+10],tans[N+10];
void tdfs(int *in,int x,int n){
	int v;
	n++; in[x]=-1;   
	if(n>scnt) return ;
	for(int i=nhead[x];i!=-1;i=nedge[i].to)
		v=nedge[i].v,in[v]--;
	repf(i, 1, scnt){
		if(in[i]==0){
			tans[n-1]=i;                   //tans[]
			tdfs(in, i, n);
			return ;
		}
	}
}
void topo(int *in){
	repf(i, 1, scnt) 
		if(in[i]==0) 
			tans[0]=i,tdfs(in,i,1);
}                                         //拓扑排序
int colo[N+10];
void color(int x){
	int v;
	colo[x]=2;
	for(int i=nhead[x];i!=-1;i=nedge[i].to){
		v=nedge[i].v;
		if(!colo[v]) color(v);
	}
}                                         //对互斥节点及子孙染色
int main(){
	int n,m,mark;
	int a,b,a0,b0;
	char c,d;
	while(1){
		cin >> n >> m ;
		if(!n&&!m) break;
		mark=0;edgeNum=0;
		clr(head,-1);clr(id,0);
		rep(i, m){
			scanf("%d%c%d%c",&a,&c,&b,&d);
			if(c=='h') a0=a+n;
			else a0=a,a+=n;
			if(d=='h') b0=b+n;
			else b0=b,b+=n;
			add(a0,b);add(b0,a);
		}
		add(0,n);
		tarjan(2*n);                             //强连通分量搞定
		//rep(i, 2*n) cout << id[i] << ' ' ;
		//cout << endl;
		int v;
		rep(i, n){
				if(id[i+n]==id[i]) mark=1;
				op[id[i]]=id[i+n];
				op[id[i+n]]=id[i];
		}                                        //构造互斥点
		if(mark) {
			cout << "bad luck" << endl;
			continue;
		}
		edgeNum=0;clr(nhead,-1);
		clr(in,0);
		rep(i, n){
			for(int w=head[i];w!=-1;w=edge[w].to){
				v=edge[w].v;
				if(id[i]!=id[v]){
					nadd(id[v],id[i]);           //边反向连接
					in[id[i]]++;
				}
			}
		}
		//repf(i, 1, 2*n) cout << in[i] << ' ' ;
		//cout << endl;
		topo(in);                                //拓扑排序搞定
		//rep(i,  scnt) cout << tans[i] << ' ' ;
		//cout << endl;
		clr(colo,0);
		rep(i, scnt){
			if(!colo[tans[i]]){
				colo[tans[i]]=1;
				color(op[tans[i]]);
			}
		}                                        //染色完毕
		repf(i, 1, n-1){
			if(colo[id[i]]==1)
				cout << i << 'h' ;
			else 
				cout << i << 'w' ;
			if(i<n-1)
				cout << ' ' ;
			else 
				cout << endl;
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值