2-SAT 挑战4.3习题

poj3678:
题目链接:
http://poj.org/problem?id=3678
题意:
给定一系列的布尔表达式,问能否找到一个解是这些都成立?
比较裸,就是对应的建立限制条件。如果i 为真,j也必须为真就建立(i,j)一条边。
要注意的就是,XOR ,比如x XOR y = 1 , 那么 等价于x=>y’ AND x’=>y AND y=>x’ AND y’=>x
x XOR y = 0 ,等价于 x=>y AND x’=>y’ AND y=>x AND y’ =>x
x AND y = 1 等价于 x’=> x AND y’=> y
一开始写错了。。对于异或只考虑了两个限制条件,居然都过了。。。可能数据弱了。。之后的一题就被这样坑了。
然后就是比较裸的了,求scc,然后判定是否有解。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
#define M 10009
vector<int> edge[M];
int DFN[M],low[M],belong[M];
bool instack[M];
stack<int> ss;
int sccnum,tot;
int n,m;
void init()
{
    for(int i = 0;i <= 2*n;i++) edge[i].clear();
    memset(DFN,0,sizeof(DFN));
    memset(low,0,sizeof(low));
    sccnum = tot = 0;
}
void add_edge(int u,int v)
{
    edge[u].push_back(v);
}
void tarjan(int u)
{
    instack[u] = true;
    DFN[u] = low[u] = ++tot;
    ss.push(u);
    for(int i = 0;i < edge[u].size();i++)
    {
        int v = edge[u][i];
        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])
    {
        int v;
        sccnum++;
        for(;;)
        {
            v = ss.top();
            ss.pop();
            instack[v] = false;
            belong[v] = sccnum;
            if(v == u) break;
        }
    }
}
void scc()
{
    for(int i = 0;i < 2*n;i++)
    {
        if(!DFN[i]) tarjan(i);
    }
}
bool solve()
{
    scc();
    for(int i = 0;i < n;i++)
    {
        if(belong[i] == belong[i+n]) return false;
    }
    return true;
}
int main()
{
    while(scanf("%d %d",&n,&m) == 2)
    {
        for(int i = 0;i < m;i++)
        {
            int a,b,c;
            char s[100];
            scanf("%d %d %d %s",&a,&b,&c,s);
            if(s[0] == 'A')
            {
                if(c == 1)
                {
                    add_edge(a+n,a);
                    add_edge(b+n,b);
                }
                if(c == 0)
                {
                    add_edge(a,b+n);
                    add_edge(b,a+n);
                }
            }
            if(s[0] == 'O')
            {
                if(c == 1)
                {
                    add_edge(a+n,b);
                    add_edge(b+n,a);
                }
                if(c == 0)
                {
                    add_edge(a,a+n);
                    add_edge(b,b+n);
                }
            }
            if(s[0] == 'X') //异或要小心
            {
                if(c == 1)
                {
                    add_edge(a,b+n);
                    add_edge(a+n,b);
                    add_edge(b+n,a);
                    add_edge(b,a+n);
                }
                if(c == 0)
                {
                    add_edge(a+n,b+n);
                    add_edge(a,b);
                    add_edge(b+n,a+n);
                    add_edge(b,a);
                }
            }
        }
        bool ok = solve();
        if(ok) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

poj2723:
题目链接:http://poj.org/problem?id=2723
题意:
有n对钥匙,对于每一对钥匙来说,如果用了其中一把钥匙,那么另一把钥匙就会直接消失,这里存在一个矛盾关系。意思就是(x,y) 属于同一对钥匙,那么就有 x => y’ AND y => x’ 。
有m个门,对于每个门来说有两把钥匙可以打开这个门,那么如果想要开这个门,同时也会产生一个矛盾关系。(x,y)属于这个门对应的两把钥匙,那么如果x为假,y就要为真,如果y为假,x就要为真。 有 x’ =>y AND y’ => x。
求最多能够打开几个门。
这就是一个最大化最小值的问题,因为题目中有说如果要打开第i个门,那么第i-1个门必须已经被打开。所以开门的顺序就有了。二分答案,对于当前的尝试能开 k 个门 进行判定,也就是加入前k个门的限定条件。如果可以打开,就提升下限。接着做。。

悲剧的是,二分写错了。。 找了半天。。在m为1的时候答案直接错了。。
我的二分写法要把上限设置成m+1,不能写成m。。(好像以前错过这个,太蠢了)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <vector>
    #include <stack>
    #include <queue>
    using namespace std;
    #define M 3000
    int DFN[M*4],low[M*4],belong[M*4];
    bool instack[M*4];
    int key1[M],key2[M],door1[M],door2[M];
    vector<int> edge[M*4];
    stack<int> ss;
    int n,m;
    int tot,sccnum;
    void init()
    {
        memset(DFN,0,sizeof(DFN));
        for(int i = 0;i < 4*n;i++) edge[i].clear();
        memset(instack,false,sizeof(instack));
        memset(belong,0,sizeof(belong));
        while(!ss.empty()) ss.pop();
        tot = sccnum = 0;
    }
    void add_edge(int u,int v)
    {
        edge[u].push_back(v);
        //edge[v].push_back(u);
    }
    void tarjan(int u)
    {
        instack[u] = true;
        DFN[u] = low[u] = ++tot;
        ss.push(u);
        for(int i = 0;i < edge[u].size();i++)
        {
            int v = edge[u][i];
            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])
        {
            int v;
            sccnum++;
            for(;;)
            {
                v = ss.top();
                ss.pop();
                instack[v] = false;
                belong[v] = sccnum;
                if(v == u) break;
            }
        }
    }
    void scc()
    {
        for(int i = 0;i < 4*n;i++)
        {
            if(!DFN[i]) tarjan(i);
        }
    }
    bool solve(int mid)
    {
        init();
        for(int i = 0;i < n;i++)
        {
            add_edge(key1[i],key2[i]+2*n);
            add_edge(key2[i],key1[i]+2*n);
        }
        for(int i = 0;i < mid;i++)
        {
            add_edge(door1[i]+2*n,door2[i]);
            add_edge(door2[i]+2*n,door1[i]);
        }
        scc();
        for(int i = 0;i < 2*n;i++)
        {
            if(belong[i] == belong[i+2*n])
                return false;
        }
        return true;
    }
    int main()
    {
        while(scanf("%d %d",&n,&m) == 2)
        {
            if(n == 0 && m == 0) break;
            for(int i = 0;i < n;i++)
                scanf("%d %d",&key1[i],&key2[i]);
            for(int i = 0;i < m;i++)
                scanf("%d %d",&door1[i],&door2[i]);
            int l = 0,r = m+1; // 要写成 m+1 不然 写成m的话m = 1 时会直接退出。。
            int ans = 0;
            while(l < r - 1)
            {
                int mid = (l+r)/2;
                if(solve(mid)) l = mid;
                else r = mid;
            }
            printf("%d\n",l);
        }
        return 0;
    }

poj2749:
题意:
要使得每个牛棚都能相互到达,建立了s1和s2两个中转站,每个牛棚都跟其中的一个中转站建立联系。但是牛棚之间还有一定的限制条件,比如有hate关系,如果a,b有hate关系,那么a、b不能和同一个中转站建立联系。还有friend关系,如果a,b是friend,那么a、b一定要在一个中转站中。
要是任意两个牛棚之间距离的最大值尽可能的小。
也是一个二分的做法
思路
那么定义布尔变量xi,如果xi 为真表示i这个牛棚与s1建立联系,为假就是与s2建立联系。
然后就可以利用之前的hate和friend关系进行建图。但是只靠这些限制条件还是不够的。
二分这个最大距离,对于任意两个点,判断所有连接方法,也就是四种情况,如果这种方法连接超过了最大了,那么就不能这样连接。增加限定条件,比如i 连 s1, j连 s2,这两个之间的距离超过了maxlen,那么就意味着如果i连s1,那么j 不能连s2, j连s2,i就不能连s1。 xi => xj AND xj’ => xi’。

之前的错误,没有想到对于任意两个点都要再次加入限定条件,只考虑了之前关系的联系,并且只想到了如果这个点到s1的距离超过maxlen,那么就一定要连s2。然而这个条件太弱了,还有要根据当前的maxlen,来限定任意两个点之间的连接方式。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
using namespace std;
#define M 100009
int DFN[M],low[M],belong[M],dis1[M],dis2[M];
int dx[M],dy[M],which[M];
bool instack[M];
vector<int> edge[M];
vector<pair<int,int> > hate,frd;
stack<int> ss;
int N,A,B;
int dis_s1_s2;
int sx1,sy1,sx2,sy2;
int tot,sccnum;
void init()
{
    memset(DFN,0,sizeof(DFN));
    memset(instack,false,sizeof(instack));
    for(int i = 1;i <= 2*N;i++) edge[i].clear();
    tot = sccnum = 0;
}
void add_edge(int u,int v)
{
    edge[u].push_back(v);
}
void tarjan(int u)
{
    instack[u] = true;
    DFN[u] = low[u] = ++tot;
    ss.push(u);
    for(int i = 0;i < edge[u].size();i++)
    {
        int v = edge[u][i];
        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])
    {
        int v;
        sccnum++;
        for(;;)
        {
            v = ss.top();
            ss.pop();
            instack[v] = false;
            belong[v] = sccnum;
            if(v == u) break;
        }
    }
}
void scc()
{
    for(int i = 1;i <= 2*N;i++)
    {
        if(!DFN[i]) tarjan(i);
    }
}
bool solve(int mid)
{
    init();
    for(int i = 0;i < hate.size();i++)
        add_edge(hate[i].first,hate[i].second);
    for(int i = 0;i < frd.size();i++)
        add_edge(frd[i].first,frd[i].second);
    for(int i = 1;i <= N;i++)
    {
        for(int j = 1;j < i;j++)
        {
            if(dis1[i] + dis1[j] > mid)
            {
                add_edge(i,j+N);
                add_edge(j,i+N);
            }
            if(dis1[i] + dis2[j] + dis_s1_s2 > mid)
            {
                add_edge(i,j);
                add_edge(j+N,i+N);
            }
            if(dis2[i] + dis1[j] + dis_s1_s2 > mid)
            {
                add_edge(i+N,j+N);
                add_edge(j,i);
            }
            if(dis2[i] + dis2[j] > mid)
            {
                add_edge(i+N,j);
                add_edge(j+N,i);
            }
        }
    }
    scc();
    for(int i = 1;i <= N;i++)
    {
        if(belong[i] == belong[i+N]) return false;
    }
    return true;
}
int main()
{
    while(scanf("%d %d %d",&N,&A,&B) == 3)
    {
        scanf("%d %d %d %d",&sx1,&sy1,&sx2,&sy2);
        dis_s1_s2 = abs(sx1-sx2) + abs(sy1-sy2);
        for(int i = 1;i <= N;i++)
        {
            scanf("%d %d",&dx[i],&dy[i]);
            dis1[i] = abs(dx[i]-sx1) + abs(dy[i]-sy1);
            dis2[i] = abs(dx[i]-sx2) + abs(dy[i]-sy2);
        }
        for(int i = 0;i < A;i++)
        {
            int a,b;
            scanf("%d %d",&a,&b);
            hate.push_back(make_pair(a,b+N));
            hate.push_back(make_pair(a+N,b));
            hate.push_back(make_pair(b+N,a));
            hate.push_back(make_pair(b,a+N));
        }
        for(int i = 0;i < B;i++)
        {
            int a,b;
            scanf("%d %d",&a,&b);
            frd.push_back(make_pair(a,b));
            frd.push_back(make_pair(a+N,b+N));
            frd.push_back(make_pair(b,a));
            frd.push_back(make_pair(b+N,a+N));
        }
        int l = 0, r = 1000000000;
        int ans = -1;
        while(l < r - 1)
        {
            int mid = (l+r)/2;
            if(solve(mid))
            {
                r = mid;
                ans = r;
            }
            else l = mid;
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(2-SAT 挑战4.3习题)