入手的第一道2-sat问题,关于怎样建图,迷糊了2天,在网上看到各种各样的解法,感到眼花缭乱,无所适从。
于是乎,直到今天才有了一点眉目。
做2-sat难在怎样建图。到目前为止,由于做的题数目有限,只能说说自己浅薄的见解。
最朴素的2-sat问题的形式是,有n个集合,每个集合中两个元素,0,1,2,…,2*n-1,从每个集合中选出一个元素,并且不同集合之间的元素之间存在某些约束,然后再根据题目求出或者判断是否能够达到某种条件。
由于是{0,1},{2,3},{4,5}…{2n-2,2n-1},所以0的对立点就是1,2的对立点就是3…那么我们在求点i的对立点的时候,就用位运算就可以求出i的对立点i^1了。我们根据题目的约束条件,进行构图。一般有下列约束条件:
A[x]
NOT A[x]
A[x] AND A[y]
A[x] AND NOT A[y]
A[x] OR A[y]
A[x] OR NOT A[y]
NOT (A[x] AND A[y])
NOT (A[x] OR A[y])
A[x] XOR A[y]
NOT (A[x] XOR A[y])
A[x] XOR NOT A[y]
在这道题中,约束条件是X OR Y,则可以建图X’->Y, Y’->X,然后就可以按照套路做了。
代码如下:
/*************************************************************************
> File Name: main.cpp
> Author:Eagles
> Mail:None
> Created Time: 2018年09月21日 星期五 18时27分27秒
> Description:
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
#define N 5000
struct mydot
{
int scc,low,dfn;
bool is_in;
void clear()
{
scc=low=dfn=-1;
is_in=false;
}
}dot[N];
struct node
{
int to;
int nex;
}E[N*N];
int head[2*N];
int hash_val[2*N];//将n对关系映射成{0,1},{2,3}之类的
int datam[N][2];
int n,m,cnt,dep,col;
stack<int>s;
void addEdge(int a, int b)
{
E[cnt].to=b;
E[cnt].nex=head[a];
head[a]=cnt++;
}
void init()
{
memset(head,-1,sizeof(head));
cnt=dep=col=0;
while (!s.empty())
s.pop();
for (int i=0; i<2*n; i++)
dot[i].clear();
}
void tarjan(int u)
{
s.push(u);
dot[u].is_in=true;
dot[u].low=dot[u].dfn=++dep;
for (int i=head[u]; i!=-1; i=E[i].nex)
{
int v=E[i].to;
if (dot[v].dfn==-1)
{
tarjan(v);
dot[u].low=min(dot[u].low,dot[v].low);
}
else if (dot[v].is_in)
dot[u].low=min(dot[v].dfn,dot[u].low);
}
if (dot[u].low == dot[u].dfn)
{
col++;
int v=s.top();
s.pop();
while (v != u)
{
dot[v].is_in=false;
dot[v].scc=col;
v=s.top();
s.pop();
}
dot[v].scc=col;
dot[v].is_in=false;
}
}
bool check()
{
for (int i=0; i<2*n; i+=2)
if (dot[i].scc == dot[i^1].scc)
return false;
return true;
}
bool solve(int mid)
{
init();
for (int i=0; i<mid; i++)
{
int u=hash_val[datam[i][0]];
int v=hash_val[datam[i][1]];
//根据约束条件加边
addEdge(u,v^1);
addEdge(v,u^1);
}
for (int i=0; i<2*n; i++)
if (dot[i].dfn==-1)
tarjan(i);
return check();
}
int main()
{
while (~scanf("%d%d",&n,&m)&&(m+n))
{
for (int i=0; i<2*n; i++)
{
int a;
scanf("%d",&a);
hash_val[a]=i;//映射
}
for (int i=0; i<m; i++)
{
scanf("%d%d",&datam[i][0],&datam[i][1]);存储约束条件
}
int l,r,mid;
l=0,r=m;
while (l+1<r)//二分查找
{
mid=(l+r)>>1;
if (solve(mid))
l=mid;
else
r=mid-1;
}
if (solve(r)) l=r;
printf("%d\n",l);
}
return 0;
}
路漫漫其修远兮,吾将上下而求索。