2-SAT问题的解法(uva1146)

SAT:就是一些由布尔值组成的关系的集合。
2-SAT:就是由两个布尔值组成的关系的集合。
2-SAT问题:就是给出一些关系,然后问能不能满足这些所有的关系?
现在比如说有n个国家,每个国家有两个代表,必须选出一个代表参加一个国际会议,但是有些代表之间有矛盾,现在给出这些矛盾的代表,问能不能选出满足条件的。
4个国家,代表编号为2*i,2 *i-1
这些代表有矛盾1和4,2和3,7和3
这样的话肯定是能够满足条件的。
这篇论文讲的很清晰

点击

其中给出了两种解法,首先对于上面的例子,1和4有矛盾,很明显分别属于1和2国家,那么加入我要选1则必须选3,因为每个国家必须选一个,而1和4又是矛盾的!同样要选4必须选2,那么我可以给他们必选条件之间建一条有向边。
那么对于上面样例可以得到这样一个图
2-SAT问题的解法(uva1146)_第1张图片
这样建图之后,能够得到一种很直观的解法。
枚举所有的同一个国家的代表(2*i 和 2 *i-1)首先任选一个,推导出相关的,若不矛盾,则可行,否则选另一个,若也不可行,则无解。
这个算法的时间负责度O(m*n),在大多数情况下是可行的。

其实可以更优,首先我们发现图中存在很多环,对环缩点是对原图情况没有影响的,同样,在同一个环中的点必然是要么都选,要么都不选,那么如果存在在一个换中有同一个国家的两个代表的话,这样肯定是不可行的。
那么就得到了一个基于对称性的算法,建图,缩点,缩在同一个环上的点判断是否是同一个国家,不在则无解。算法负责度O(m)

UVA1146这个题目是一个类似的题目,不过要求一个最大值,我们二分结果,然后用2-set判断是否可行。
Tarjan算法:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int N = 2200;
int tim[N][3],dfs_clock,tpnum;
stack<int> sta;
struct TwoSet
{
    vector<int> G[2*N];
    bool vis[2*N];
    int dfn[2*N],low[2*N],tp[2*N];
    void init(int n)
    {
        for(int i=0;i<=2*n;i++)
            G[i].clear();
        dfs_clock = tpnum = 0;
        memset(vis,false,sizeof(vis));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(tp,0,sizeof(tp));
        while(!sta.empty())
            sta.pop();

    }
    void add_Node(int x,int valx,int y,int valy)
    {
        x = 2*x + valx;
        y = 2*y + valy;
        G[x^1].push_back(y);
        G[y^1].push_back(x);
    }
    void Tarjan(int x)
    {
        sta.push(x);
        vis[x] = true;
        dfn[x] = low[x] = ++dfs_clock;
        for(int i=0;iint y = G[x][i];
            if(!dfn[y])
            {
                Tarjan(y);
                low[x] = min(low[x],low[y]);
            }
            else
            {
                if(vis[y])
                    low[x] = min(low[x],dfn[y]);
            }
        }
        if(low[x] == dfn[x])
        {
            tpnum++;
            do
            {
                x = sta.top();
                sta.pop();
                vis[x] = false;
                tp[x] = tpnum;
            }while(low[x] != dfn[x]);
        }
    }
    bool yougth(int n)
    {
        for(int i=0;i<2*n;i++)
            if(!dfn[i])
                Tarjan(i);
        for(int i=0;iif(tp[2*i]==tp[2*i+1])
                return false;
        return true;
    }
};
TwoSet solver;
bool test(int diff,int n)
{
    solver.init(n);
    for(int i=0;ifor(int a=0;a<2;a++)
        for(int j=i+1;jfor(int b=0;b<2;b++)
            if(abs(tim[i][a]-tim[j][b])1,j,b^1);
    return solver.yougth(n);
}
int main()
{
    //freopen("Input.txt","r",stdin);
    int n;
    while(~scanf("%d",&n))
    {
        int L = 0,R = 0;
        for(int i=0;ifor(int t=0;t<2;t++)
            {
                scanf("%d",&tim[i][t]);
                R = max(R,tim[i][t]);
            }
        }
        while(Lint mid=L+(R-L+1)/2;
            if(test(mid,n))L=mid;
            else R=mid-1;
        }
        printf("%d\n",L);
    }
    return 0;
}

直接暴力判断算法:

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int N = 2200;
int tim[N][3];

struct TwoSet
{
    vector<int> G[2*N];
    bool vis[2*N];
    int s[2*N],c;
    void init(int n)
    {
        for(int i=0;i<=2*n;i++)
            G[i].clear();
        memset(vis,false,sizeof(vis));
    }
    void add_Node(int x,int valx,int y,int valy)
    {
        x = 2*x + valx;
        y = 2*y + valy;
        G[x^1].push_back(y);
        G[y^1].push_back(x);
    }
    bool dfs(int x)
    {
        if(vis[x^1])
            return false;
        if(vis[x])
            return true;
        vis[x] = true;
        s[c++] = x;
        for(int i=0;iif(!dfs(G[x][i]))
                return false;
        }
        return true;
    }
    bool yougth(int n)
    {
        for(int i = 0;i< 2*n;i+=2)
        {
            if(!vis[i] && !vis[i+1])
            {
                c = 0;
                if(!dfs(i))
                {
                    while(c>0)
                        vis[ s[--c] ] = false;
                    if(!dfs(i+1))
                        return false;
                }
            }
        }
        return true;
    }
};
TwoSet solver;
bool test(int diff,int n)
{
    solver.init(n);
    for(int i=0;ifor(int a=0;a<2;a++)
        for(int j=i+1;jfor(int b=0;b<2;b++)
            if(abs(tim[i][a]-tim[j][b])1,j,b^1);
    return solver.yougth(n);
}
int main()
{
    //freopen("Input.txt","r",stdin);
    int n;
    while(~scanf("%d",&n))
    {
        int L = 0,R = 0;
        for(int i=0;ifor(int t=0;t<2;t++)
            {
                scanf("%d",&tim[i][t]);
                R = max(R,tim[i][t]);
            }
        }
        while(Lint mid=L+(R-L+1)/2;
            if(test(mid,n))L=mid;
            else R=mid-1;
        }
        printf("%d\n",L);
    }
    return 0;
}

你可能感兴趣的:(2-set)