图论3:2-sat

ORZ

どこでもドア
一个我当年学2-sat的博客。

2-sat

现在来口胡一下。
2-sat问题大致就是每个人有两个选项,又有一些形如某人选了选项1/2另某个人就必须/不能选选项1/2的限制,求一种满足所有限制的选择方案。
2以上的sat问题被证明是np完全的,而2-sat有O(n)的做方法。

做法

对每个选了a就必须选b的限制,a向b连边(同时就有b’向a’连边),然后先塔尖再拓排即可。
塔尖强联通缩点之后,若x和x’在同一强连通分量内显然无解,否则一定能构造出合法解。
给出一种合法的构造方式,缩点后把原图的边反向连,拓排,每次选择出队的点x并把x’和所有x’前面的点删除设为不能选。

感性口胡

1)因为如果存在边a->b一定存在边b’->a’,故图存在很强的对称性,一个强联通分量内的点的相反点也在同一个强连通分量内,故缩点后构造方案和原图方案等价。
2)缩点后的DAG上通过上诉拓排一定能构造出合法解。
1.不能选的点已经被删除了,所以拓排当前出队的点一定能选。
2.不存在x和x’同时被删除。若x在a被选的时候被删除,x’在b被选的时候被删除,设a先于b被选。x->a’(a->x’),x’->b’(b->x),即a被选时b也会被删。

题目

bzoj1823: [JSOI2010]满汉全席

どこでもドア
2-sat模板题,仅判断是否合法。

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
const int N=1007; 
typedef long long LL;
using namespace std;
int T,n,m,bel[N],dfn[N],low[N];

template<typename T> void read(T &x) {
    char ch=getchar(); x=0; T f=1;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

int ecnt,fir[N],nxt[20007],to[20007];
void add(int u,int v) {
    nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
}

int get() {
    int x; char o=getchar();
    while(o!='h'&&o!='m') o=getchar(); read(x);
    return o=='h'?(x<<1):(x<<1|1);
}

int que[N],dfs_clock,top,tot;
void tarjan(int x) {
    dfn[x]=low[x]=++dfs_clock;
    que[++top]=x;
    for(int i=fir[x];i;i=nxt[i]) {
        if(!dfn[to[i]]) {
            tarjan(to[i]);
            low[x]=min(low[x],low[to[i]]);
        }
        else if(!bel[to[i]]) low[x]=min(low[x],dfn[to[i]]);
    }
    if(dfn[x]==low[x]) {
        tot++;
        while(top) {
            int tp=que[top--];            
            bel[tp]=tot;
            if(tp==x) break;
        }
    }
}

int main() {
#ifdef DEBUG
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    read(T);
    while(T--) {
        read(n); read(m); ecnt=0;
        memset(dfn,0,sizeof(dfn));
        memset(bel,0,sizeof(bel));
        memset(fir,0,sizeof(fir));
        for(int i=1;i<=m;i++) {
            int x=get();
            int y=get();
            add(x^1,y); add(y^1,x);
        }
        dfs_clock=tot=0;
        for(int i=2;i<=(n<<1|1);i++) if(!dfn[i]) tarjan(i);
        int fl=0;
        for(int i=2;i<=(n<<1|1);i+=2) 
            if(bel[i]==bel[i|1]) {
                fl=1; break;
            }
        if(fl) puts("BAD");
        else puts("GOOD"); 
    }
    return 0;
}

POJ - 3683 Priest John’s Busiest Day

どこでもドア
有n对人,每对有两个时间段可以选,问所有人的时间不重叠的方案。
2-sat模板,输出方案

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
const int N=2007;
typedef long long LL;
using namespace std;
int n,st[N],ed[N];

template<typename T> void read(T &x) {
    char ch=getchar(); x=0; T f=1;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

int ecnt,fir[N],nxt[N*N],to[N*N];
void add(int u,int v) {
    nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
}

int cnt,fi[N],nx[N*N],tt[N*N],in[N];
void ADD(int u,int v) {
    nx[++cnt]=fi[u]; fi[u]=cnt; tt[cnt]=v; in[v]++;
}

int xj(int x,int y) {
    return !(ed[x]<=st[y]||st[x]>=ed[y]);
}

int tot,dfs_clock,dfn[N],low[N],que[N],top,bl[N];
void tarjan(int x) {
    que[++top]=x;
    dfn[x]=low[x]=++dfs_clock;
    for(int i=fir[x];i;i=nxt[i]) {
        if(!dfn[to[i]]) {
            tarjan(to[i]);
            low[x]=min(low[x],low[to[i]]);
        }
        else if(!bl[to[i]]) low[x]=min(low[x],dfn[to[i]]); 
    }
    if(low[x]==dfn[x]) {
        tot++;
        while(top) {
            int tp=que[top--];
            bl[tp]=tot;
            if(tp==x) break;
        }
    }
}

int vis[N],op[N]; 
void dfs(int x) {
    if(vis[x]) return;
    vis[x]=-1;
    for(int i=fi[x];i;i=nx[i]) 
        dfs(tt[i]);
}

void tpsort() {
    top=0;
    for(int i=1;i<=tot;i++)
        if(!in[i]) que[++top]=i;
    while(top) {
        int x=que[top--];
        if(vis[x]) continue;
        vis[x]=1; dfs(op[x]);
        for(int i=fi[x];i;i=nx[i]) {
            int y=tt[i];
            in[y]--;
            if(!in[y]) que[++top]=y;
        }
    } 
}

void print(int x) {
    printf("%.2d:",x/60);
    printf("%.2d ",x%60);
}

int main() {
#ifdef DEBUG
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    read(n);
    for(int i=1;i<=n;i++) {
        int x,y,z;
        read(x); read(y);
        st[i<<1]=x*60+y;
        read(x); read(y);
        ed[i<<1|1]=x*60+y;
        read(z); 
        ed[i<<1]=st[i<<1]+z;
        st[i<<1|1]=ed[i<<1|1]-z;
    }
    for(int i=2;i<=2*n+1;i++) 
        for(int j=i+1;j<=2*n+1;j++) 
            if(i!=j&&(i!=(j^1))&&xj(i,j))
                add(i,j^1),add(j,i^1);
    for(int i=2;i<=2*n+1;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++) {
        if(bl[i<<1]==bl[i<<1|1]) {
            puts("NO"); return 0;
        }
        else {
            op[bl[i<<1]]=bl[i<<1|1];
            op[bl[i<<1|1]]=bl[i<<1];
        }
    }
    puts("YES");
    for(int i=2;i<=2*n+1;i++) 
        for(int j=fir[i];j;j=nxt[j])
            if(bl[i]!=bl[to[j]]) 
                ADD(bl[to[j]],bl[i]);
    tpsort();
    for(int i=1;i<=n;i++) {
        if(vis[bl[i<<1]]==1) {
            print(st[i<<1]); printf(" "); print(ed[i<<1]); puts("");
        } 
        else {
            print(st[i<<1|1]); printf(" "); print(ed[i<<1|1]); puts("");
        } 
    }
    return 0;
}

[NOI2017]游戏

どこでもドア
暴力枚举每种x当作a,b,c中的哪一种,然后2-sat判断即可.

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=400007;
typedef long long LL; 
typedef double db;
using namespace std;
int n,d,m,no[10],col[N],tid[N][4]; // A:1 B:2 C:3
char s[N],ss[10];

template<typename T> void read(T &x) {
    char ch=getchar(); x=0; T f=1;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

struct node {
    int a,b,x,y;
}q[N];

int ecnt,fir[N],nxt[N<<1],to[N<<1];
void add(int u,int v) {
    nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
}

int dfs_clock,dfn[N],low[N],bl[N],op[N],tot,sta[N],top;
void tarjan(int x) {
    dfn[x]=low[x]=++dfs_clock;
    sta[++top]=x;
    for(int i=fir[x];i;i=nxt[i]) {
        if(!dfn[to[i]]) {
            tarjan(to[i]);
            low[x]=min(low[x],low[to[i]]);
        }
        else if(!bl[to[i]]) low[x]=min(low[x],dfn[to[i]]);
    }
    if(low[x]==dfn[x]) {
        tot++;
        for(;;) {
            int y=sta[top--];
            bl[y]=tot;
            if(y==x) break;
        }
    }
}

int fi[N],nx[N],tt[N],ec,in[N];
void ADD(int u,int v) {
    nx[++ec]=fi[u]; fi[u]=ec; tt[ec]=v; in[v]++;
}

queue<int>que;
int vis[N];
void dfs(int x) {
    vis[x]=-1;
    for(int i=fi[x];i;i=nx[i]) if(!vis[tt[i]]) 
        dfs(tt[i]);
}

void tpsort() {
    For(i,1,tot) if(!in[i]) que.push(i);
    while(!que.empty()) {
        int x=que.front();
        que.pop();
        if(vis[x]) continue;
        vis[x]=1;
        dfs(op[x]);
        for(int i=fi[x];i;i=nx[i]) {
            in[tt[i]]--;
            if(!in[tt[i]]) que.push(tt[i]);
        } 
    }        
}

int main() {
#ifdef DEBUG
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    read(n); read(d);
    scanf("%s",s+1);
    read(m);
    For(i,1,m) {
        read(q[i].a); scanf("%s",ss); q[i].x=ss[0]-'A'+1;
        read(q[i].b); scanf("%s",ss); q[i].y=ss[0]-'A'+1;
    }
    int up=(3<<d)-1;
    For(ti,0,up) {
        For(i,1,n*2) fir[i]=0,dfn[i]=0,bl[i]=0,vis[i]=0; 
        int tp=ti; ecnt=0; tot=0;
        For(i,1,d) {
            no[i]=tp%3; tp/=3;
        }
        int pos=0;
        For(i,1,n) {
            if(s[i]=='x') pos++;
            if(s[i]=='a'||(s[i]=='x'&&no[pos]==0)) tid[i][1]=0,tid[i][2]=i,tid[i][3]=i+n,col[i]=2,col[i+n]=3;
            if(s[i]=='b'||(s[i]=='x'&&no[pos]==1)) tid[i][1]=i,tid[i][2]=0,tid[i][3]=i+n,col[i]=1,col[i+n]=3;
            if(s[i]=='c'||(s[i]=='x'&&no[pos]==2)) tid[i][1]=i,tid[i][2]=i+n,tid[i][3]=0,col[i]=1,col[i+n]=2;
        }
        For(i,1,m) {
            int a=q[i].a,x=q[i].x,b=q[i].b,y=q[i].y;
            if(a==b&&x==y) continue;
            if(tid[a][x]) {
                if(tid[b][y]) {
                    add(tid[a][x],tid[b][y]);
                    int z=tid[a][x]==a?a+n:a;
                    int w=tid[b][y]==b?b+n:b;
                    add(w,z);
                }
                else {
                    int z;
                    For(j,1,3) if(tid[a][j]&&j!=x) z=tid[a][j];
                    add(tid[a][x],z);
                }
            }
        }
        For(i,1,n*2) if(!dfn[i]) tarjan(i);
        int ok=1;
        For(i,1,n) {
            if(bl[i]==bl[i+n]) {
                ok=0; break;
            }
            op[bl[i]]=bl[i+n];
            op[bl[i+n]]=bl[i];
        }
        if(ok) {
            For(i,1,n*2) {
                for(int j=fir[i];j;j=nxt[j]) if(bl[to[j]]!=bl[i]) 
                    ADD(bl[to[j]],bl[i]);
            }
            tpsort();
            For(i,1,n) {
                if(vis[bl[i]]==1) printf("%c",'A'+col[i]-1);
                else printf("%c",'A'+col[i+n]-1);
            }
            puts("");
            return 0;
        }
    }
    puts("-1");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值