解释
对于布尔方程(aVbVc…….)^(dVeVf……)^………,我们称(aVbVc…….),(dVeVf……)为子句,a,b,c,d,e,f…..为文字,若每个子句的文字均不超过两个,那么求解这样的方程被称为2-SAT.
此算法可以利用强连通分量高效求解这类方程.
实现方法
若aVb=1,我们可以发现当!a时,b必须为1,当!b时,a必须为1,也就是说,若!a为真,则b为真,若!b为真,则a为真.
如果方程内有n个文字,则我们可以建2*n个点,i和i+n两点分别表示i=1和i=0两种状态,因此两种状态必然一真一假,若a状态可以推出b状态,则a向b连一条有向边.
可以发现,对于这张图上的强连通分量,每个点全为真或全为假,若a和!a出现在同一个强连通分量中则说明此方程无解,反之有解.
输出方案
对这张图进行拓扑排序(因而建议用Kosaraju求强连通分量),若x所在的强连通分量的拓扑序在!x所在的强连通分量之后,则x为真.
例题 poj 3683
题意
有n件事情要做,每件事情可以在两个时间段中选一个时间段做,问是否可以将n将事情做完.
做法
首先如果在第一个时间段做则为真,反之为假,可以根据时间段间的矛盾来建边,若a,b两时间段有交集,则a向!b,b向!a连边,最后求强连通分量即可.
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 2010
#define M 4001000
using namespace std;
int n,s[2][N],t[N],bb,first[N],num[N],tim[N],tt,lt,aa,a[M],b[M];
bool vis[N],ans[N];
struct Bn
{
int to,next;
}bn[M];
inline void add(int u,int v)
{
a[++aa]=u;
b[aa]=v;
bb++;
bn[bb].to=v;
bn[bb].next=first[u];
first[u]=bb;
}
inline void ad(int u,int v)
{
bb++;
bn[bb].to=v;
bn[bb].next=first[u];
first[u]=bb;
}
void dfs(int now)
{
int p,q;
vis[now]=1;
for(p=first[now];p!=-1;p=bn[p].next)
{
if(!vis[bn[p].to]) dfs(bn[p].to);
}
tim[++tt]=now;
}
void Dfs(int now)
{
num[now]=lt;
vis[now]=1;
int p,q;
for(p=first[now];p!=-1;p=bn[p].next)
{
if(!vis[bn[p].to]) Dfs(bn[p].to);
}
}
inline bool scc()
{
int i;
for(i=1;i<=n;i++)
{
if(!vis[i]) dfs(i);
}
memset(first,-1,sizeof(first));
bb=0;
for(i=1;i<=aa;i++)
{
ad(b[i],a[i]);
}
memset(vis,0,sizeof(vis));
for(i=tt;i>=1;i--)
{
if(!vis[tim[i]])
{
lt++;
Dfs(tim[i]);
}
}
for(i=1;i<=n;i++)
{
if(num[i]==num[i+n]) return 0;
}
return 1;
}
int main()
{
memset(first,-1,sizeof(first));
int i,j,a,b,c,d;
cin>>n;
for(i=1;i<=n;i++)
{
scanf("%d:%d %d:%d%d",&a,&b,&c,&d,&t[i]);
s[0][i]=a*60+b;
s[1][i]=c*60+d-t[i];
}
for(i=1;i<=n;i++)
{
for(j=i+1;j<=n;j++)
{
if(min(s[0][i]+t[i],s[0][j]+t[j])>max(s[0][i],s[0][j]))
{
add(i,j+n);
add(j,i+n);
}
if(min(s[1][i]+t[i],s[0][j]+t[j])>max(s[1][i],s[0][j]))
{
add(i+n,j+n);
add(j,i);
}
if(min(s[0][i]+t[i],s[1][j]+t[j])>max(s[0][i],s[1][j]))
{
add(i,j);
add(j+n,i+n);
}
if(min(s[1][i]+t[i],s[1][j]+t[j])>max(s[1][i],s[1][j]))
{
add(i+n,j);
add(j+n,i);
}
}
}
if(!scc())
{
puts("NO");
return 0;
}
puts("YES");
for(i=1;i<=n;i++)
{
if(num[i]>num[i+n]) ans[i]=1;
}
for(i=1;i<=n;i++)
{
ans[i]?printf("%02d:%02d %02d:%02d\n",s[0][i]/60,s[0][i]%60,(s[0][i]+t[i])/60,(s[0][i]+t[i])%60):printf("%02d:%02d %02d:%02d\n",s[1][i]/60,s[1][i]%60,(s[1][i]+t[i])/60,(s[1][i]+t[i])%60);
}
}