图论学习——2-SAT

SAT——Satisfiability

2-SAT问题介绍:
一串布尔变量,每个变量只能为真或假。要求对这些变量进行赋值,满足布尔方程。

例题:
1.P4782 【模板】2-SAT 问题

点击查看代码块
/*
-代表非的意思,n为点(变量)的个数
a或b—————— -a->b 且 -b->a  对应加边:(a+n,b),(b+n,a)
-a或b—————— a->b 且 -b->-a 对应加边:(a,b),(b+n,a+n)
a或-b—————— -a->-b 且 b->a 对应加边:(a+n,b+n),(b,a)
-a或-b—————— a->-b 且 b->-a 对应加边:(a,b+n),(b,a+n)
*/
#include <bits/stdc++.h>

#define ed end()
#define bg begin()
#define mkp make_pair
#define pb push_back
#define v(T) vector<T>
#define all(x) x.bg,x.ed
#define newline puts("")
#define si(x) ((int)x.size())
#define rep(i,n) for(int i=1;i<=n;++i)
#define rrep(i,n) for(int i=0;i<n;++i)
#define srep(i,s,t) for(int i=s;i<=t;++i)

using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e6+10;
const int inf = 0x7f7f7f7f;
const ll inf_ll = 1ll*inf*inf;
const int Mod = 1e9+7;
const double eps = 1e-7;

int n,m;
int head[maxn],cnt=0;
struct edge{
    int v,next;
}e[maxn<<1];
void add(int u,int v){
    e[cnt].v=v;e[cnt].next=head[u];head[u]=cnt++;
}
int dfn[maxn],low[maxn];
bool instack[maxn];
stack<int> st;
int ct = 0,book[maxn],num[maxn],tot = 0;

void tarjan(int u){
    low[u]=dfn[u]=++tot;
    instack[u]=1;
    st.push(u);
    for (int i=head[u];~i;i=e[i].next){
        int v=e[i].v;
        if(!dfn[v]) {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u] == low[u]){
        ++ct;
        while(1){
            int x=st.top();
            st.pop();
            book[x]=ct;
            num[ct]++;
            instack[x]=0;
            if(x == u) break;
        }
    }
}
int main(){
    memset(head,-1,sizeof(head));
    cnt=0;
    cin>>n>>m;
    for (int i=1;i<=m;i++){
        int ii,a,jj,b;
        scanf("%d%d%d%d",&ii,&a,&jj,&b);
        if(a && b){//a或b
            add(ii+n,jj);add(jj+n,ii);
        }
        else if(!a && b){//-a或b
            add(ii,jj);add(jj+n,ii+n);
        }
        else if(a && !b){//a或b
            add(ii+n,jj+n);add(jj,ii);
        }
        else if(!a && !b){
            add(ii,jj+n);add(jj,ii+n);
        }
    }
    // for (int i=1;i<=m;i++){
    //     int ii,a,jj,b;
    //     scanf("%d%d%d%d",&ii,&a,&jj,&b);
    //     add(ii+n*(a&1),jj+n*(b^1));
    //     add(jj+n*(b&1),ii+n*(a^1));
    // }
    for (int i=1;i<=2*n;i++){
        if(!dfn[i]) tarjan(i);
    }
    // for (int i=1;i<=2*n;i++){
    //     printf("i = %d book = %d
",i,book[i]);
    // }
    for (int i = 1; i <= n; ++i)
    if (book[i] == book[i + n]) { // x 与 -x 在同一强连通分量内,一定无解
        puts("IMPOSSIBLE");
        return 0;
    }
    puts("POSSIBLE");
    for (int i = 1; i <= n; ++i){
        if(book[i] < book[i + n]){
            printf("1 "); // 如果不使用 Tarjan 找环,改成大于号
        }
        else printf("0 ");
    }
    newline;
    return 0;
}
/*
3 1
1 1 3 0
*/

2.P4171 [JSOI2010]满汉全席

点击查看代码块
/*
我们设满式菜品m为1,汉式菜品h为0,
*/
#include <bits/stdc++.h>

#define ed end()
#define bg begin()
#define mkp make_pair
#define pb push_back
#define v(T) vector<T>
#define all(x) x.bg,x.ed
#define newline puts("")
#define si(x) ((int)x.size())
#define rep(i,n) for(int i=1;i<=n;++i)
#define rrep(i,n) for(int i=0;i<n;++i)
#define srep(i,s,t) for(int i=s;i<=t;++i)

using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e4+10;
const int inf = 0x7f7f7f7f;
const ll inf_ll = 1ll*inf*inf;
const int Mod = 1e9+7;
const double eps = 1e-7;
int n,m;

int head[maxn],cnt=0;
struct edge{
    int v,next;
}e[maxn<<1];
void add(int u,int v){
    e[cnt].v=v;e[cnt].next=head[u];head[u]=cnt++;
}
int dfn[maxn],low[maxn],tot,ct,book[maxn];
int instack[maxn];
stack<int> st;

int num = 0;
void init(){
    cnt=0;
    memset(head,-1,sizeof(head));
    num=tot=ct=0;
    for (int i=1;i<=2*n;i++){
        low[i]=dfn[i]=book[i]=instack[i]=0;
    }   
}

void tarjan(int u){
    dfn[u]=low[u]=++tot;
    instack[u]=1;
    st.push(u);
    for (int i=head[u];~i;i=e[i].next){
        int v=e[i].v;
        if(!dfn[v]) {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u]){
        ++ct;
        while(1){
            int x=st.top();
            st.pop();
            book[x]=ct;
            instack[x]=0;
            if(x==u) break;
        }
    }
}
int main(){
    int T;
    scanf("%d",&T);
    for(int _=1;_<=T;_++)
    {
        while(!st.empty()) st.pop();
        scanf("%d%d",&n,&m);
        init();
        for (int i=1;i<=m;i++){
            string s1,s2;
            cin>>s1>>s2;
            int u=0,v=0;
            for (int j=1;j<s1.size();j++){
                u=u*10+s1[j]-'0';
            }
            for (int j=1;j<s2.size();j++){
                v=v*10+s2[j]-'0';
            }
//            cout<<"u = "<<u<<" v = "<<v<<endl;
            if(s1[0]=='m' && s2[0]=='m'){
                add(u+n,v),add(v+n,u);
            }
            else if(s1[0]=='m' && s2[0]=='h'){
                add(u+n,v+n),add(v,u);
            }
            else if(s1[0]=='h' && s2[0]=='m'){
                add(u,v),add(v+n,u+n);
            }
            else if(s1[0]=='h' && s2[0]=='h'){
                add(u,v+n),add(v,u+n);
            }
        }
        for (int i=1;i<=(n*2);i++){
            if(!dfn[i]) tarjan(i);
        }
        int flag = 1;
        for (int i=1;i<=n;i++){
            if(book[i]==book[i+n]){
                puts("BAD");
                flag = 0;
                break;
            }
        }
        if(flag) puts("GOOD");
    }
    return 0;
}

3.P6378 [PA2010]Riddle
题意:
n 个点 m 条边的无向图被分成 k 个部分。每个部分包含一些点。
请选择一些关键点,使得每个部分恰有一个关键点,且每条边至少有一个端点是关键点。

点击查看代码块
/*
两限制:
每个部分中只能有一个关键点,每条边的两端必须有一个关键点
优化点集内建边算法
我们设prei表示该部分中前i个点(包括第i个)是否为关键点,
则有:
ai->prei, -prei->-ai
pre(i-1)->prei, -prei->-pre(i-1)
pre(i-1)->-ai, ai->-pre(i-1)

prei表示为ai+2*n, -prei表示为ai+3*n
*/
#include <bits/stdc++.h>

#define ed end()
#define bg begin()
#define mkp make_pair
#define pb push_back
#define v(T) vector<T>
#define all(x) x.bg,x.ed
#define newline puts("")
#define si(x) ((int)x.size())
#define rep(i,n) for(int i=1;i<=n;++i)
#define rrep(i,n) for(int i=0;i<n;++i)
#define srep(i,s,t) for(int i=s;i<=t;++i)

using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e6+10;
const int inf = 0x7f7f7f7f;
const ll inf_ll = 1ll*inf*inf;
const int Mod = 1e9+7;
const double eps = 1e-7;
int n,m,k;
int head[maxn<<2],cnt=0;
struct edge{
    int v,next;
}e[maxn<<4];
void add(int u,int v){
    e[cnt].v=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
int pre[maxn<<1];
int dfn[maxn<<2],low[maxn<<2],instack[maxn<<2],book[maxn<<2];
int tot=0,ct=0;
stack<int> st;

void tarjan(int u){
    dfn[u]=low[u]=++tot;
    instack[u]=1;
    st.push(u);
    for (int i=head[u];~i;i=e[i].next){
        int v=e[i].v;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        ++ct;
        while(1){
            int x=st.top();
            st.pop();
            instack[x]=0;
            book[x]=ct;
            if(x == u) break;
        }
    }
}
bool check(){
    for (int i=1;i<=n;i++){
        if(book[i] == book[i+n] || book[i+2*n] == book[i+3*n]) return false;
    }
    return true;
}
int main(){
    memset(head,-1,sizeof(head));
    cnt=0;

    cin>>n>>m>>k;
    for (int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u+n,v),add(v+n,u);
    }
    
    for (int i=1;i<=k;i++){
        int w;
        scanf("%d",&w);
        for (int j=1;j<=w;j++){
            scanf("%d",&pre[j]);
            add(pre[j],pre[j]+2*n),add(pre[j]+3*n,pre[j]+n);
        }
        for (int j=2;j<=w;j++){
            add(pre[j-1]+2*n,pre[j]+2*n),add(pre[j]+3*n,pre[j-1]+3*n);
            add(pre[j-1]+2*n,pre[j]+n),add(pre[j],pre[j-1]+3*n);
        }
    }
    for (int i=1;i<=(n<<2);i++){
        if(!dfn[i]) tarjan(i);
    }
    if(check()) puts("TAK");
    else puts("NIE");
    return 0;
}

4.TV Show Game
题意:
给定若干个人,每个人三次猜测某些灯的颜色(红色或者蓝色),如果一个人的三次猜测中有两个或两个以上是正确的,
则这个人能获得一个礼物。
问是否存在一种灯的颜色组合使得所有人都能获得一个礼物

点击查看代码块
/*
给定若干个人,每个人三次猜测某些灯的颜色(红色或者蓝色),如果一个人的三次猜测中有两个或两个以上是正确的,
则这个人能获得一个礼物。
问是否存在一种灯的颜色组合使得所有人都能获得一个礼物

RRR R表示1,B表示0(7~0)
RRB
RBR
RBB
BRR
BRB
BBR
BBB
*/
#include <bits/stdc++.h>

#define ed end()
#define bg begin()
#define mkp make_pair
#define pb push_back
#define v(T) vector<T>
#define all(x) x.bg,x.ed
#define newline puts("")
#define si(x) ((int)x.size())
#define rep(i,n) for(int i=1;i<=n;++i)
#define rrep(i,n) for(int i=0;i<n;++i)
#define srep(i,s,t) for(int i=s;i<=t;++i)

using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 1e5+10;
const int inf = 0x7f7f7f7f;
const ll inf_ll = 1ll*inf*inf;
const int Mod = 1e9+7;
const double eps = 1e-7;
int head[maxn],cnt=0;
struct edge{
    int v,next;
}e[maxn<<4];
void add(int u,int v){
    e[cnt].v=v;e[cnt].next=head[u];head[u]=cnt++;
}
void init(){
    memset(head,-1,sizeof(head));cnt=0;
}
int n,m;
int dfn[maxn<<1],low[maxn],instack[maxn],book[maxn];
int tot=0,ct=0;
stack<int>st;

void tarjan(int u){
    dfn[u]=low[u]=++tot;
    instack[u]=1;
    st.push(u);
    for (int i=head[u];~i;i=e[i].next){
        int v=e[i].v;
        if(!dfn[v]) {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        ++ct;
        while(1){
            int x=st.top();st.pop();
            book[x]=ct;
            instack[x]=0;
            if(x == u) break;
        }
    }
}

int check(char a,char b,char c){
    return (((a=='R')<<2)+((b=='R')<<1)+(c=='R'));
}
int main(){
    // cout<<check('R','R','R')<<endl;
    init();
    cin>>n>>m;
    int a,b,c;
    char ca,cb,cc;
    for (int i=1;i<=m;i++){
        cin>>a>>ca>>b>>cb>>c>>cc;
        int op = check(ca,cb,cc);
        if(7 == op){//RRR
            add(a+n,b),add(a+n,c);
            add(b+n,a),add(b+n,c);
            add(c+n,a),add(c+n,b);
        }
        else if(6 == op){//RRB
            add(a+n,b),add(a+n,c+n);
            add(b+n,a),add(b+n,c+n);
            add(c,a),add(c,b);
        }
        else if(5 == op){//RBR
            add(a+n,b+n),add(a+n,c);
            add(b,a),add(b,c);
            add(c+n,a),add(c+n,b+n);
        }
        else if(4 == op){//RBB
            add(a+n,b+n),add(a+n,c+n);
            add(b,a),add(b,c+n);
            add(c,a),add(c,b+n);
        }
        else if(3 == op){//BRR
            add(a,b),add(a,c);
            add(b+n,a+n),add(b+n,c);
            add(c+n,a+n),add(c+n,b);
        }
        else if(2 == op){//BRB
            add(a,b),add(a,c+n);
            add(b+n,a+n),add(b+n,c+n);
            add(c,a+n),add(c,b);
        }
        else if(1 == op){//BBR
            add(a,b+n),add(a,c);
            add(b,a+n),add(b,c);
            add(c+n,a+n),add(c+n,b+n);
        }
        else if(0 == op){//BBB
            add(a,b+n),add(a,c+n);
            add(b,a+n),add(b,c+n);
            add(c,a+n),add(c,b+n);
        }
    }
    for (int i=1;i<=2*n;i++){
        if(!dfn[i]) tarjan(i);
    }
    for (int i=1;i<=n;i++){
        if(book[i] == book[i+n]){
            puts("-1");
            return 0;
        }
    }
    for (int i=1;i<=n;i++){
        if(book[i]<book[i+n]){
            printf("R");
        }
        else printf("B");
    }
    newline;
    return 0;
}

5.P3825 [NOI2017]游戏

#include <bits/stdc++.h>

#define mkp make_pair
#define pb push_back
#define v(T) vector<T>
#define all(x) x.bg,x.ed
#define newline puts("")
#define si(x) ((int)x.size())
#define rep(i,n) for(int i=1;i<=n;++i)
#define rrep(i,n) for(int i=0;i<n;++i)
#define srep(i,s,t) for(int i=s;i<=t;++i)

using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 5e5+10;
const int inf = 0x7f7f7f7f;
const int Mod = 1e9+7;
const double eps = 1e-7;

int n,m,d;
int x[maxn],y[maxn];
char c1[maxn],c2[maxn],c[maxn];
int pos[maxn],k = 0;//x的位置

int head[maxn],cnt=0;
struct edge{
    int v,next;
}e[maxn<<1];
void add(int u,int v){
    e[cnt].v=v;e[cnt].next=head[u];head[u]=cnt++;
}

int dfn[maxn],book[maxn],low[maxn];
bool instack[maxn];
int tot,ct;
stack<int> st;

void init(){
    memset(e,0,sizeof(e));
    for (int i=0;i<maxn;i++){
        head[i]=-1;
        dfn[i]=low[i]=book[i]=0;
        instack[i]=0;
    }
    while(!st.empty()) st.pop();
    tot=ct=cnt=0;
}

void tarjan(int u){
    dfn[u]=low[u]=++tot;
    instack[u]=1;
    st.push(u);
    for (int i=head[u];~i;i=e[i].next){
        int v=e[i].v;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        ++ct;
        while(1){
            int x=st.top();
            st.pop();
            instack[x]=0;
            book[x]=ct;
            if(u == x) break;
        }
    }
}
char Ans[maxn];
bool check(){
    for (int i=1;i<=2*n;i++){
        if(!dfn[i]) tarjan(i);
    }
    for (int i=1;i<=n;i++){
        if(book[i] == book[i+n]) return false;
        if(book[i]<book[i+n]){
            Ans[i] = (c[i]=='A')?'B':'A';
        }
        else Ans[i] = (c[i]=='C')?'B':'C';
    }
    for (int i=1;i<=n;i++){
        printf("%c",Ans[i]);
    }
    return true;
}
void solve(){
    for (int i=0;i<(1<<d);i++){//枚举2的d次方
        init();
        for (int j=1;j<=d;j++){
            c[pos[j]]=(i & (1<<(j-1))) ? 'A' : 'B';//pos[j]表示第j个x地图的位置,判断每个地图的情况
        }
        for (int j=1;j<=m;j++){
            if(c1[j] == c[x[j]]) continue;//如果第i场比赛不能用hi,显然关系无法成立
            if(c2[j] == c[y[j]]) {
                if(c1[j] == 'C' || (c1[j] == 'B' && c[x[j]] == 'C'))
                /*如果第jj场比赛不能用hj,那么这条关系不能成立,即i不能选hi,只能选择另一辆可行的车, 
                此时根据前面关系数组的建立,判断hi是第一辆还是第二辆可行的车,将其连到另一个(意为必须选另一个)*/
                add(x[j] + n, x[j]);
                else add(x[j], x[j] + n);
                continue;
            }
            //关系成立需要建立(hi->hj),(`hj->`hi)
            int add1 , add2;
            if(c1[j] == 'C' || (c1[j] == 'B' && c[x[j]] == 'C')) add1 = n;
            else add1 = 0;
            if(c2[j] == 'C' || (c2[j] == 'B' && c[y[j]] == 'C')) add2 = n;
            else add2 = 0;
            add(x[j] + add1, y[j] + add2);  //注意建正反两条边
            add(y[j] - add2 + n, x[j] - add1 + n); //从正边到反边可以用-add + n 得到,可手模理解一下
        }
        if(check()) exit(0);
    }
    printf("-1");
    return ;
}
int main(){
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    init();
    cin>>n>>d;
    cin>>(c+1);
    cin>>m;
    for (int i=1;i<=m;i++){
        scanf("%d %c %d %c",&x[i],&c1[i],&y[i],&c2[i]);
    }
    for (int i=1;i<=n;i++){
        c[i]-=32;
        if(c[i] == 'X') pos[++k] = i;//记录地图x的位置
    }
    solve();
    return 0;
}
点击查看代码块
你将不再是道具,而是成为人如其名的人
原文地址:https://www.cnblogs.com/wsl-lld/p/13589135.html