这道2-SAT题目比较适合刚学习该算法的人去做。
SAT被称为布尔方程的可满足性问题,即给出一个布尔方程,判断是否有一组布尔变量的真值指派能使方程为真。SAT问题是一个NP问题,但是给它加上一定限制条件之后,就可以有效求解,而2-SAT问题就是在限制之后有有效求解方法的一类问题。
对于2-SAT的题目,我们要将问题转换为一个布尔表达式,并且形式是合取范式,即(a∨b∨...)∧(c∨d∨...)∧...的形式。实际上2-SAT问题每个子句最多有两个布尔变量,即(a∨b)∧(c∨d)∧...的形式。要这种形式的布尔方程为真,那么每个子句都必须为真,所以将问题转换成布尔方程时,只要写出所有限制条件即可,即出现不符合的情况,一定有一个子句为假,其他允许的情况为真。(详细可以参考《挑战程序设计竞赛》 人民邮电出版社 P324)
转换成布尔方程,是求解2-SAT问题的第一步,也是最重要的一步,接下来的几步可以说对于2-SAT问题都是差不多的。第二步,则是用得到的布尔方程构图。针对每一个子句,可以建立两条边,例如子句a∨b,则可得到边¬a ->b和¬b ->a。第三步,在得到的图上使用强连通算法,得到强连通分量,如果¬a和a属于同一个强连通分量,则不存在解,后面就不必继续了,否则将分量缩为一个点(即将分量用一个点代表),得到DAG图。第四步,对DAG拓扑排序。然后后根据拓扑序号进行如下判断:如果¬a所在分量的拓扑序号在a所在分量的拓扑序号的前面,则a为真,否则为假。
看起来挺复杂的,实际上不会像上面说的那样麻烦,可以采用一些方法简化一下。此外,如果上面的符号看不懂的话,建议先看一下离散数学关于布尔表达式的概念。
在求强连通分量时,用kosaraju算法,那么就不需要拓扑排序(目前我不知道用其他强连通算法是不是也可以有办法免去拓扑排序)。因为kosaraju算法得到强连通分量的标号就是一个拓扑排序的标号,强连通分量标号小的会指向强连通分量标号大的。
代码(C++):
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define MAX 1009
using namespace std;
//#define LOCAL
struct Edge{
int v;
int next;
} edge[MAX*MAX*4],redge[MAX*MAX*4];
int s[MAX],t[MAX],d[MAX];
int head[MAX*2],rhead[MAX*2],scc[MAX*2],ts[MAX*2],k,c,p,n;
bool vis[MAX*2];
void add_edge(int u,int v)
{
edge[c].v=v;
edge[c].next=head[u];
head[u]=c;
redge[c].v=u;
redge[c].next=rhead[v];
rhead[v]=c;
c++;
}
void dfs(int u)
{
int i,v;
vis[u]=true;
ts[++k]=u;
for(i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(!vis[v]) dfs(v);
}
}
void rdfs(int u)
{
int i,v;
vis[u]=true;
scc[u]=p;
for(i=rhead[u];i!=-1;i=redge[i].next)
{
v=redge[i].v;
if(!vis[v]) rdfs(v);
}
}
void kosaraju()
{
int i;
k=0;
memset(vis,false,sizeof(vis));
for(i=0;i<2*n;i++)
{
if(!vis[i]) dfs(i);
}
p=1;
memset(vis,false,sizeof(vis));
for(i=k;i>0;i--)
{
if(!vis[ts[i]])
{
rdfs(ts[i]);
p++;
}
}
}
int main(int argc, char *argv[])
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int h1,h2,m1,m2,i,j;
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
{
scanf("%d:%d %d:%d %d",&h1,&m1,&h2,&m2,&d[i]);
//cout<<h1<<':'<<m1<<'\t'<<h2<<':'<<m2<<'\t'<<d[i]<<endl;
s[i]=h1*60+m1;
t[i]=h2*60+m2;
}
c=0;
memset(head,-1,sizeof(head));
memset(rhead,-1,sizeof(rhead));
for(i=0;i<n;i++)
{
for(j=i+1;j<n;j++)
{
if(max(s[i],s[j])<min(s[i]+d[i],s[j]+d[j]))
{
add_edge(i,n+j);
add_edge(j,n+i);
}
if(max(t[i]-d[i],t[j]-d[j])<min(t[i],t[j]))
{
add_edge(n+i,j);
add_edge(n+j,i);
}
if(max(s[i],t[j]-d[j])<min(s[i]+d[i],t[j]))
{
add_edge(i,j);
add_edge(j+n,i+n);
}
if(max(t[i]-d[i],s[j])<min(t[i],s[j]+d[j]))
{
add_edge(i+n,j+n);
add_edge(j,i);
}
}
}
//cout<<"c = "<<c<<endl;
kosaraju();
//cout<<"p = "<<p<<endl;
for(i=0;i<n;i++)
{
if(scc[i]==scc[i+n]) break;
}
if(i<n)
{
printf("NO\n");
continue;
}
printf("YES\n");
for(i=0;i<n;i++)
{
if(scc[i]>scc[i+n])
{
printf("%02d:%02d %02d:%02d\n",s[i]/60,s[i]%60,(s[i]+d[i])/60,(s[i]+d[i])%60);
}else{
printf("%02d:%02d %02d:%02d\n",(t[i]-d[i])/60,(t[i]-d[i])%60,t[i]/60,t[i]%60);
}
}
}
system("PAUSE");
return EXIT_SUCCESS;
}
题目( http://poj.org/problem?id=3683):
Time Limit: 2000MS | Memory Limit: 65536K | |||
Special Judge |
Description
John is the only priest in his town. September 1st is the John's busiest day in a year because there is an old legend in the town that the couple who get married on that day will be forever blessed by the God of Love. This year N couples plan to get married on the blessed day. The i-th couple plan to hold their wedding from time Si to time Ti. According to the traditions in the town, there must be a special ceremony on which the couple stand before the priest and accept blessings. The i-th couple need Di minutes to finish this ceremony. Moreover, this ceremony must be either at the beginning or the ending of the wedding (i.e. it must be either from Si to Si + Di, or from Ti - Di to Ti). Could you tell John how to arrange his schedule so that he can present at every special ceremonies of the weddings.
Note that John can not be present at two weddings simultaneously.
Input
The first line contains a integer N ( 1 ≤ N ≤ 1000).
The next N lines contain the Si, Ti and Di. Si and Ti are in the format of hh:mm.
Output
The first line of output contains "YES" or "NO" indicating whether John can be present at every special ceremony. If it is "YES", output another N lines describing the staring time and finishing time of all the ceremonies.
Sample Input
2 08:00 09:00 30 08:15 09:00 20
Sample Output
YES 08:00 08:30 08:40 09:00