zoukankan      html  css  js  c++  java
  • 图论学习——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;
    }
    
    点击查看代码块
    你将不再是道具,而是成为人如其名的人
  • 相关阅读:
    20191324第一周读书笔记
    20191324《信息安全专业导论》第十一周学习总结
    20191324《信息安全专业导论》第九周学习总结
    20191324《信息安全专业导论》第八周学习总结
    20191324《信息安全专业导论》第七周学习总结
    20182317 2018-2019-1《程序设计与数据结构》课程总结
    20182317 2019-2020-1 《数据结构与面向对象程序设计》实验九报告
    学号20182317 2019-2020-1 《数据结构与面向对象程序设计》第十周学习总结
    # 20182317 2019-2020-1 哈夫曼测试
    学号20182317 2019-2020-1 《数据结构与面向对象程序设计》第九周学习总结
  • 原文地址:https://www.cnblogs.com/wsl-lld/p/13589135.html
Copyright © 2011-2022 走看看