poj 3683

         这道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):

Priest John's Busiest Day
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 SiTi and DiSi 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值