码迷,mamicode.com
首页 > 其他好文 > 详细

POJ 3683 2SAT

时间:2016-07-29 22:44:47      阅读:200      评论:0      收藏:0      [点我收藏+]

标签:

这道题涨了很多姿势。

首先,以后可能不需要重新建图+拓扑排序+染色了。

看了赵爽的论文。

下面说的简略一些,详细情况可自行百度。

首先是重新建图,重新建图为的是求拓扑序,以便于染色,出答案。而本身我们tarjan完的就是个拓扑逆序的。因为每个团求出来之前,它之后的所有团肯定都求出来,否则这个团是全都在栈里的。

所以,拓扑序和重新建图是不需要的。然后是染色,染色是为了出答案。

关于可行性判定就不说了,观察答案,考虑到,每对点都要被挑一个(否则不可行),而且肯定是挑拓扑序小的那个,因为大的是永远不可能被选到的。分情况证明,首先,如果挑的时候,序小的和大的两团都在,那自然挑大的;小的在,大的不在,自然挑小的;大的在,小的不在,这种情况不可能出现,因为如果大的在之前被排除掉了,那么序小的这个肯定在之前就被挑过了,因为有证明,如果i,j在一个团里,那么i‘,j‘也在一个团里。

下面是代码,以后可以更进一步了。

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<cmath>
#include<cstdlib>

using namespace std;

const int MAXN=2010;
struct marry
{
    int fr;
    int ed;
}date[MAXN];
struct node
{
    int v;
    int nxt;
};
node edge[MAXN*MAXN];//边数
int head[MAXN];
int n,m;
int Stop,Bcnt,Dindex;//栈头,强通块数,时间戳
int DFN[MAXN],LOW[MAXN];//首时间戳,最近回溯点(根)
int Stap[MAXN];//答案栈
int instack[MAXN];//是否在栈中
int Belong[MAXN];//这个点属于第几个强连通块(点)
int cnt=0;
void add_edge(int u,int v)
{
    edge[cnt].v=v;
    edge[cnt].nxt=head[u];
    head[u]=cnt;
    cnt++;
}
void tarjan(int i)
{
    int j;
    DFN[i]=LOW[i]=++Dindex;
    instack[i]=1;
    Stap[++Stop]=i;
    for (int e=head[i]; e!=-1; e=edge[e].nxt)
    {
        j=edge[e].v;
        if (!DFN[j])//儿子没遍历
        {
            tarjan(j);//遍历
            if (LOW[j]<LOW[i])//如果儿子已经形成环
                LOW[i]=LOW[j];//父亲也要在回溯的时候进入环
        }
        else if (instack[j]&&DFN[j]<LOW[i])//邻接的在栈里,所以是大大
            LOW[i]=DFN[j];//把这个点归到大大那
    }
    if (DFN[i]==LOW[i])//这个点的根是自己
    {
        Bcnt++;//多了一个强连通分量
        do
        {
            j=Stap[Stop--];//退栈
            instack[j]=0;//标记
            Belong[j]=Bcnt;//标记
        }
        while (j!=i);
    }
}
int solve()
{
    int i;
    Stop=Bcnt=Dindex=0;//栈头,强通块数,时间戳
    memset(DFN,0,sizeof(DFN));
    for (int i=0; i<2*n; i++)
    {
//        printf ("dfn[i]\%d\n",DFN[i]);
        if (!DFN[i])
            tarjan(i);
    }
    for (int i=0;i<2*n;i+=2)//不在一个联通块里
    {
        if (Belong[i]==Belong[i+1]) return 0;
    }
    return 1;
}
int against(marry a,marry b)
{
    if (a.fr<=b.fr&&a.ed>b.fr) return 1;
    if (a.fr>=b.fr&&a.fr<b.ed) return 1;
    return 0;
}
int main()
{
    while (scanf ("%d",&n)!=EOF)
    {
        for (int i=0;i<2*n;i+=2)
        {
            int h1,m1,h2,m2,tmp;
            scanf ("%d:%d%d:%d%d",&h1,&m1,&h2,&m2,&tmp);
            date[i].fr=h1*60+m1;
            date[i].ed=h1*60+m1+tmp;

            date[i+1].fr=h2*60+m2-tmp;
            date[i+1].ed=h2*60+m2;
        }
        memset(head,-1,sizeof(head));
        for (int i=0;i<2*n;i++)
        {
            for (int j=i+1;j<2*n;j++)
            {
                if (i!=(j^1)&&against(date[i],date[j]))
                {
                    add_edge(i,j^1);
                    add_edge(j,i^1);
                }
            }
        }
        if (!solve()) printf ("NO\n");
        else
        {
            printf ("YES\n");
            for (int i=0;i<2*n;i+=2)
            {
                if (Belong[i]<Belong[i+1])
                {
                    printf ("%02d:%02d %02d:%02d\n",date[i].fr/60,date[i].fr%60,date[i].ed/60,date[i].ed%60);
                }
                else
                    printf ("%02d:%02d %02d:%02d\n",date[i+1].fr/60,date[i+1].fr%60,date[i+1].ed/60,date[i+1].ed%60);
            }
        }
    }
    return 0;
}

 

POJ 3683 2SAT

标签:

原文地址:http://www.cnblogs.com/nj-czy/p/5719752.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!