2-sat:

一、染色法,暴力染色,求解字典序最小解:
HDU1814
有n对人,每对有两个,有m对相互讨厌的关系,相互讨厌的两个人不能同时出现,每对人中出现且只能出现一个,判断能否成立,若成立,输出字典序最小的解。
2-sat水题:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn=21000;
const int maxm=110000;
int head[maxn],ver[maxm],nt[maxm];
bool ha[maxn];
int st[maxn];
int tot=1,top=0;

void add(int x,int y)
{
    ver[++tot]=y,nt[tot]=head[x],head[x]=tot;
}

bool dfs(int x)
{
    if(ha[x^1]) return false;
    if(ha[x]) return true;
    ha[x]=true;
    st[++top]=x;
    for(int i=head[x];i;i=nt[i])
    {
        if(!dfs(ver[i]))
            return false;
    }
    return true;
}

bool is_can(int n)
{
    memset(ha,0,sizeof(ha));
    for(int i=2;i<=n;i+=2)
    {
        if(ha[i]||ha[i^1]) continue;
        top=0;
        if(!dfs(i))
        {
            while(top) ha[st[top--]]=false;
            if(!dfs(i^1)) return false;
        }
    }
    return true;
}

int main(void)
{
    int n,m;
    int x,y;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(head,0,sizeof(head));
        tot=1;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            x++,y++;
            add(x,y^1);
            add(y,x^1);
        }
        if(is_can(n*2+1))
        {
            for(int i=2;i<=2*n+1;i++)
            {
                if(ha[i])
                    printf("%d\n",i-1);
            }
        }
        else printf("NIE\n");
    }
    return 0;
}

二、Priest John’s Busiest Day POJ - 3683 :
本题中,每场婚礼是一个变量,有开始时举行仪式和结束前举行仪式两种取值。
然后,就是一个2-sat问题了,本题不要求字典序最小的解,拓扑排序即可实现:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn=2100;
const int maxm=4010000;
int head[maxn],ver[maxm],nt[maxm],dfn[maxn],low[maxn],c[maxn];
int head2[maxn],ver2[maxm],nt2[maxm],d[maxn],opp[maxn];
int st[maxn],s[maxn],t[maxn],dd[maxn],val[maxn];
bool ha[maxn];
int n,m,tot,tot2,top,cnt,sum;
void init(void)
{
    memset(head,0,sizeof(head));
    memset(head2,0,sizeof(head2));
    memset(ha,0,sizeof(ha));
    memset(d,0,sizeof(d));
    memset(dfn,0,sizeof(dfn));
    tot=tot2=1;
    top=cnt=sum=0;
}

void add(int x,int y)
{
    ver[++tot]=y,nt[tot]=head[x],head[x]=tot;
}
void add_c(int x,int y)
{
    ver2[++tot2]=y,nt2[tot2]=head2[x],head2[x]=tot2;
}

bool is_can(int b1,int e1,int b2,int e2)
{
    if(b1>=b2&&b1<e2||e1>b2&&e1<=e2||b1<=b2&&e1>=e2) return true;
    if(b2>=b1&&b2<e1||e2>b1&&e2<=e1||b2<=b1&&e2>=e1) return true;
    return false;
}

void tarjan(int x)
{
    dfn[x]=low[x]=++sum;
    st[++top]=x,ha[x]=true;

    for(int i=head[x];i;i=nt[i])
    {
        if(!dfn[ver[i]])
        {
            tarjan(ver[i]);
            low[x]=min(low[x],low[ver[i]]);
        }
        else if(ha[ver[i]])
            low[x]=min(low[x],low[ver[i]]);
    }

    if(dfn[x]==low[x])
    {
        cnt++;
        int y;
        do
        {
            y=st[top--];
            ha[y]=false;
            c[y]=cnt;
        }while(x!=y);
    }
}


void tp(void)
{
    memset(val,-1,sizeof(val));
    for(int x=1;x<=n*2;x++)
        for(int i=head[x];i;i=nt[i])
        {
            int y=ver[i];
            if(c[x]==c[y]) continue;
            add_c(c[y],c[x]);
            d[c[x]]++;
        }
    queue<int>q;
    for(int i=1;i<=cnt;i++)
    {
        if(!d[i]) q.push(i);
    }

    while(q.size())
    {
        int x=q.front();
        q.pop();
        if(val[x]==-1) val[x]=0,val[opp[x]]=1;
        for(int i=head2[x];i;i=nt2[i])
        {
            d[ver2[i]]--;
            if(d[ver2[i]]==0) q.push(ver2[i]);
        }
    }
    return ;
}






int main(void)
{
    while(scanf("%d",&n)!=EOF)
    {
        init();
        int sh,sm,th,tm;
        for(int i=1;i<=n;i++)
        {
            scanf("%d:%d%d:%d%d",&sh,&sm,&th,&tm,&dd[i]);
            s[i]=sh*60+sm,t[i]=th*60+tm;
        }

        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<i;j++)
            {
                if(is_can(s[i],s[i]+dd[i],s[j],s[j]+dd[j]))
                    add(i,n+j),add(j,n+i);
                if(is_can(s[i],s[i]+dd[i],t[j]-dd[j],t[j]))
                    add(i,j),add(n+j,n+i);
                if(is_can(t[i]-dd[i],t[i],s[j],s[j]+dd[j]))
                    add(n+i,n+j),add(j,i);
                if(is_can(t[i]-dd[i],t[i],t[j]-dd[j],t[j]))
                    add(n+i,j),add(n+j,i);
            }
        }

        for(int i=1;i<=2*n;i++)
        {
            if(!dfn[i]) tarjan(i);
        }

        bool flag=false;
        for(int i=1;i<=n;i++)
        {
            if(c[i]==c[i+n])
            {
                flag=true;
                printf("NO\n");
                break;
            }
            opp[c[i]]=c[n+i],opp[c[n+i]]=c[i];
        }

        if(flag) continue;

        printf("YES\n");
        tp();
        for(int i=1;i<=n;i++)
        {
            if(val[c[i]]==0)
            {
                printf("%02d:%02d %02d:%02d\n",s[i]/60,s[i]%60,(s[i]+dd[i])/60,(s[i]+dd[i])%60);
            }
            else
            {
                printf("%02d:%02d %02d:%02d\n",(t[i]-dd[i])/60,(t[i]-dd[i])%60,t[i]/60,t[i]%60);
            }
        }
    }

    return 0;
}

还可以利用有向图求强连通分量的性质,直接获得拓扑序:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn=2100;
const int maxm=4010000;
int head[maxn],ver[maxm],nt[maxm],dfn[maxn],low[maxn],c[maxn];
int d[maxn],opp[maxn];
int st[maxn],s[maxn],t[maxn],dd[maxn],val[maxn];
bool ha[maxn];
int n,m,tot,tot2,top,cnt,sum;
void init(void)
{
    memset(head,0,sizeof(head));
    memset(ha,0,sizeof(ha));
    memset(d,0,sizeof(d));
    memset(dfn,0,sizeof(dfn));
    tot=tot2=1;
    top=cnt=sum=0;
}

void add(int x,int y)
{
    ver[++tot]=y,nt[tot]=head[x],head[x]=tot;
}


bool is_can(int b1,int e1,int b2,int e2)
{
    if(b1>=b2&&b1<e2||e1>b2&&e1<=e2||b1<=b2&&e1>=e2) return true;
    if(b2>=b1&&b2<e1||e2>b1&&e2<=e1||b2<=b1&&e2>=e1) return true;
    return false;
}

void tarjan(int x)
{
    dfn[x]=low[x]=++sum;
    st[++top]=x,ha[x]=true;

    for(int i=head[x];i;i=nt[i])
    {
        if(!dfn[ver[i]])
        {
            tarjan(ver[i]);
            low[x]=min(low[x],low[ver[i]]);
        }
        else if(ha[ver[i]])
            low[x]=min(low[x],low[ver[i]]);
    }

    if(dfn[x]==low[x])
    {
        cnt++;
        int y;
        do
        {
            y=st[top--];
            ha[y]=false;
            c[y]=cnt;
        }while(x!=y);
    }
}


int main(void)
{
    while(scanf("%d",&n)!=EOF)
    {
        init();
        int sh,sm,th,tm;
        for(int i=1;i<=n;i++)
        {
            scanf("%d:%d%d:%d%d",&sh,&sm,&th,&tm,&dd[i]);
            s[i]=sh*60+sm,t[i]=th*60+tm;
        }

        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<i;j++)
            {
                if(is_can(s[i],s[i]+dd[i],s[j],s[j]+dd[j]))
                    add(i,n+j),add(j,n+i);
                if(is_can(s[i],s[i]+dd[i],t[j]-dd[j],t[j]))
                    add(i,j),add(n+j,n+i);
                if(is_can(t[i]-dd[i],t[i],s[j],s[j]+dd[j]))
                    add(n+i,n+j),add(j,i);
                if(is_can(t[i]-dd[i],t[i],t[j]-dd[j],t[j]))
                    add(n+i,j),add(n+j,i);
            }
        }

        for(int i=1;i<=2*n;i++)
        {
            if(!dfn[i]) tarjan(i);
        }

        bool flag=false;
        for(int i=1;i<=n;i++)
        {
            if(c[i]==c[i+n])
            {
                flag=true;
                printf("NO\n");
                break;
            }
            opp[i]=n+i,opp[n+i]=i;
        }

        if(flag) continue;

        printf("YES\n");
        for(int i=1;i<=n*2;i++)
        {
            val[i]=c[i]>c[opp[i]];
        }

        for(int i=1;i<=n;i++)
        {
            if(val[i]==0)
            {
                printf("%02d:%02d %02d:%02d\n",s[i]/60,s[i]%60,(s[i]+dd[i])/60,(s[i]+dd[i])%60);
            }
            else
            {
                printf("%02d:%02d %02d:%02d\n",(t[i]-dd[i])/60,(t[i]-dd[i])%60,t[i]/60,t[i]%60);
            }
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值