什么是2-SAT?
其实2-SAT,就是有很多组人,每组有只有两个成员,要你从每组中选一个人。其中,有一些人有矛盾不能同时选。如果抽象的说,就是有n个集合,每个集合有两个元素,从每个集合中选出一个元素,其中有一些元素不能同时选。判定是否有一种方案可以满足上列的条件。
2-SAT的主要问题?
是否有解,如果有解给出一组可行解,有时有要求字典序最小的解。
主要算法
对这类问题进行构造建图。下面进行一些约定:我们把假定有i 组人,第i组的两个成员分别为Ai 和 Ai'。如果要选Ai就必须选Aj,那么就把 Ai 指向 Aj。如果Ai和Aj有矛盾,则选Ai 就必须选Aj',选Aj 就必须选Ai'。特别的,如果Ai 和 Ai'中,一定不选Ai,就连一条由Ai指向Ai'的边。2-SAT问题可以转为求有向图的强连通分量和拓扑排序(具体见
由对称性解2-SAT问题)。
下面是算法的步骤:
1、根据题意建图
2、求强连通分量(
这有一篇大神写的强连通分量的文章)
3、拓扑求可行解
算法的模板:
如果有n组人,有m条边那么下面算法的效率为O(m)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//如果不求方案数就要把下面这句删掉
#define REQUE_COLOR 1
//writen by nothi
#ifdef REQUE_COLOR
const int RED = 1;
const int BULE = 2;
const int WHITE = 0;
#endif
const int maxn = 1100;
//顶点的个数,这里的顶点个数实际上就是组的个数
//组的编号从0到n-1
const int maxn2 = maxn*2;
const int maxm = maxn*maxn;//边的条数
typedef struct Edge{
int s;
int e;
int next;
}Edge;
typedef struct Adj{
int edge_sum;
int head[maxn2];
Edge edge[maxm];
void initial(){
edge_sum = 0;
memset(head,-1,sizeof(head));
}
void add_edge(int a, int b){
edge[edge_sum].s = a;
edge[edge_sum].e = b;
edge[edge_sum].next = head[a];
head[a] = edge_sum++;
}
}Adj;
#ifdef REQUE_COLOR
Adj op_adj;
#endif
//顶点序号(组的编号)从0到n-1
//编号为i的组,在实际的算法中组员是2*i 和 2*i+1
//所以对于组员i,和它同组的另一个组员为i^1
//color[belong[u]] == RED 是解
//选调用initial()初始化,再用solve()解决问题
//如果要求一组可行解,调用creat();
typedef struct Two_sat{
int n;//n是顶点数
Adj *adj;
Edge *e;
int *head;
int top, cnt, cur;
int dfn[maxn2];
int low[maxn2];
int stack[maxn2];
int belong[maxn2];
int instack[maxn2];
void initial(Adj* _adj,int _n){
n = _n;
adj = _adj;
e = (*adj).edge;
head = (*adj).head;
}
bool check(){
int i, j;
tarjan();
for(i = 0; i < 2*n; i += 2)
if(belong[i] == belong[i^1])
return false;
return true;
}
int min(int a, int b){
if(a < b) return a;
else return b;
}
void tarjan(int i){
int j = head[i];
dfn[i] = low[i] = ++cur;
instack[i] = 1;
stack[top++] = i;
while(j != -1){
int u = e[j].e;
if (dfn[u] == -1){
tarjan(u);
low[i] = min(low[i],low[u]);
}else if (instack[u])
low[i] = min(low[i],dfn[u]);
j = e[j].next;
}
if(dfn[i] == low[i]){
++cnt;
do{
j = stack[--top];
instack[j] = 0;
belong[j] = cnt;
}while(j != i);
}
}
void tarjan(){
int i, j;
cur = cnt = top = 0;
memset(dfn,-1,sizeof(dfn));
for(i = 0; i < 2*n; i++)
if(dfn[i] == -1)
tarjan(i);
}
#ifdef REQUE_COLOR
//求具体的方案
int opp[maxn2];
int color[maxn2];
int indegree[maxn2];
void creat(){
int u, v, i, j;
memset(indegree,0,sizeof(indegree));
op_adj.initial();
for(i = 0; i < 2*n; i += 2){
opp[belong[i]] = belong[i^1];
opp[belong[i^1]] = belong[i];
}
for(i = 0; i < (*adj).edge_sum; i++){
u = belong[e[i].s];
v = belong[e[i].e];
if(u == v) continue;
++indegree[u];
op_adj.add_edge(v,u);
}
toposort();
}
void toposort(){
int *head1 = op_adj.head;
Edge *edge = op_adj.edge;
int i, j, p, now, head, tail,next;
head = tail = 0;
memset(color,WHITE,sizeof(color));
for(i = 1; i <= cnt; i++)
if(!indegree[i])
stack[++head] = i;
while(head != tail){
now = stack[++tail];
if(color[now] != WHITE) continue;
color[now] = RED;
color[opp[now]] = BULE;
p = head1[now];
while(p != -1){
next =edge[p].e;
--indegree[next];
if(indegree[next] == 0){
stack[++head] = next;
}
p = edge[p].next;
}
}
}
#endif
}Two_sat;
Adj adj;
Two_sat ts;相关问题:
直接的判定问题:
二分答案+2-SAT:
求一组可行解:
求字典序最小的方案:
这题可以看
这里
1449

被折叠的 条评论
为什么被折叠?



