【转】2-SAT题目总结

关于2-SAT(2-Satisfiability)资料的话就是伍昱的《由对称性解2-SAT问题》PPT和赵爽的《2-SAT 解法浅析》PDF。

关于2-SAT的模板可参考[1]、[2]

简要意思就是给定N个组(每个组2个元素)、M个互斥关系,从每个组里挑1个使得给定的不满足任何互斥关系。

但是解决这类问题的关键还是在于建模,基本建模就是对于两个不相容的点i、j,构图方式为:i->j'(i和j冲突,选i只能选j')和j->i'(i和j冲突,选j只能选i')。

解2-SAT方法是,对原图求一次强连通分量,然后看每组中的两个点是否属于同一个强连通分量,如果存在这种情况,那么无解。

下面是我做过的几道2SAT问题,持续更新。

[HDU3622]Bomb Game

其 实如果知道什么是2-SAT的话这题就变得很裸了。用2*i表示i组的第一个炸弹,2*i+1表示i组的第二个炸弹,然后二分最大半径r,每个组里选一个 炸弹,对于不同组的两个炸弹i和炸弹j只要有dis[i][j] <= 2*r的话那么炸弹i就与炸弹j冲突,连边i->j^1和j->i^1再验证2-SAT即可。

 

bool check(double r)//对于两个不相容的点i,j 构图方式为:i->j'和j->i'
{
    init();
    for (int i = 0;i < 2*n; i++)
        for (int j=i+1;j < 2*n;j++)
        {
            if ((i>>1) != (j>>1) && dis[i][j] <= r*2)            
       { addedge(i,j
^1); addedge(j,i^1); } } return TwoSat(); }

 

 

 

[SRM 464 DIV1 500]ColorfulDecoration

跟上题几乎一模一样,只不过变成正方形,二分边长s, 验证|x1 - x2| ≥ s or |y1 - y2| ≥ s。

 

[POI2001]Peaceful Commission [POI2001]和平委员会

一样的两题,不过我的某个代码只能过一个,不知道是那个数据有问题,这个就是PPT上的例题,不仅要验证2-SAT,还要输出一个方案,输出方案具体的参见PDF。

 

[PKU3207]Ikki's Story IV - Panda's Trick

题意:平面上有一个圆,圆的边上按顺时针放着0..n-1共n个点。现在要连m条边,比如a,b,那么a到b可以从圆的内部连接,也可以从圆的外部连接。给你的信息中,每个点最多只能连一条边。问是否可以连接这m条边,使这些边都不相交。

对 于每条边有两个选择:内部或者外部,而且边与边之间有关系,于是2-SAT,把一条边拆成2个点,2*i表示i边放在内部,2*i+1表示i边放在外部, 当边i与边j冲突时建立2*i<->2*j+1、2*j<->2*i+1,验证2-SAT即可。

inline bool conflict(int i,int j)
{
    bool flag = 0;
    flag |= ((x[j] > x[i] && x[j] < y[i]) && !(y[j] > x[i] && y[j] < y[i]));
    flag |= ((y[j] > x[i] && y[j] < y[i]) && !(x[j] > x[i] && x[j] < y[i]));
    return flag;
}

void build()//对于两个不相容的点i,j 构图方式为:i->j'和j->i'
{
    init();
    for (int i = 0;i < n;i++)
        for (int j = i+1;j < n;j++)
        {
            if (conflict(i,j))
            {
                addedge(2*i,2*j+1);
                addedge(2*j+1,2*i);
                addedge(2*j,2*i+1);
                addedge(2*i+1,2*j);
            }
        }
}

 

[PKU2296]Map Labeler

题意:给定n个点,在这n个点上方或者下方可以放一个平行x、y轴的矩形(点在矩形的边的中点),问能放完n个矩形(不相交)的最大边长是多少。

首 先是二分答案,然后是比较麻烦的构图,要分好几种情况讨论,先设2*i:矩形放点i上面 2*i+1:矩形放点i下面,那么首先当abs(x[i]-x[j]) >= r时必定是可以任意放的。而当abs(x[i]-x[j]) < r时:①abs(y[i]-y[j]) < r:当y[i] == y[j]时两个点的矩形可以一上一下,否则只能是上面的点的矩形放上面,下面的点的矩形放下面。②abs(y[i]-y[j]) < 2*r:除了上面的点的矩形放下面,下面的点的矩形放上面的情况都是可以的,按此构图即可。

 

bool check(int r)//对于两个不相容的点i,j 构图方式为:i->j'和j->i'
{
    init();
    //2*i:矩形放点i上面 2*i+1:矩形放点i下面
    for (int i = 0;i < n; i++)
        for (int j = i+1;j < n;j++) {
            if(abs(x[i]-x[j]) < r) {
                if (abs(y[i]-y[j]) < r) {
                    if (y[i] == y[j]) {
                        addedge(2*i+1,2*j);
                        addedge(2*j,2*i+1);
                        addedge(2*j+1,2*i);
                        addedge(2*i,2*j+1);
                    }
                    else if (y[i] > y[j]) {
                        addedge(2*i,2*i+1);
                        addedge(2*j+1,2*j);
                    }
                    else {
                        addedge(2*j,2*j+1);
                        addedge(2*i+1,2*i);
                    }
                }
                else if (abs(y[i]-y[j]) < 2*r) {
                    if (y[i] > y[j]) {
                        addedge(2*i,2*j);
                        addedge(2*j+1,2*i+1);
                    }
                    else {
                        addedge(2*j,2*i);
                        addedge(2*i+1,2*j+1);
                    }
                }
            }
        }
    return TwoSat();
}

 

 

 

[PKU3648]Wedding

这个题较麻烦,题意是

“n-1对夫 妇去参加一对新人的婚礼。人们坐在一个很长很长的桌子的两侧(面对面)。新郎新娘在桌子一头面对面座。新娘不希望看见她对面的一排有一对夫妇坐着(所有夫 妇需要分开两排座)。同时,一些人之间有暧昧关系,新娘也不希望有暧昧关系的人同时坐在她对面的一排(但是可以同时和她并排)。问能否满足新娘的要求,可 以的话,输出一种方案。”

首先这题蕴含很多冲突关系,这 时我们就应该要向2-SAT的方面思考下了。首先,每个人都可以坐在桌子的左右两边。那么我们把每个人拆成两个点,坐左边用i表示,坐右边的用i'表示。 然后再定义第i对夫妇的2*i表示女的,2*i+1表示男的。那么i' = i + 2*n。也就是2*i:女左 2*i+1:男左 2*i+2*n:女右 2*i+1+2*n:男右。

因为有暧昧关系的人可以跟新娘坐一排那么我们让新娘坐左边(左右一样),为了防止有新娘左右边的解出现我们初始化:

addedge(2*0+2*n,2*0);(如果新娘坐右边我们就必须同时让新娘坐左边,显然是矛盾的,故加了这句后新娘永远也不会坐到右边了)。
addedge(2*0+1,2*0+1+2*n);(同理新郎坐右边)。

对于所有的夫妇有:选男左就得选女右,选女左就得选男右,选男右就得选女左,选女右就得选男左。

for (int i = 0;i < n;i++)
{
     addedge(2*i,2*i+1+2*n);
     addedge(2*i+1,2*i+2*n);
     addedge(2*i+2*n,2*i+1);
     addedge(2*i+1+2*n,2*i);
}

对于m个暧昧关系有:选了男右的就不能选女右,选了女右就不能选男右。

for (int i = 0;i < m;i++)
{
    scanf("%d%c%d%c",&x,&c1,&y,&c2);
    x = 2*x + (c1 == 'h');
    y = 2*y + (c2 == 'h');
    addedge(x+2*n,y);
    addedge(y+2*n,x);
}

最后验证2-SAT再求出一个解就好了~。

 

[PKU3678]Katu Puzzle

题意:一些点,点的取值可以是0或者1,一些边,有权值,有运算方式(AND,OR,XOR),要求和这条边相连的两个点经过边上的运算后的结果是边的权值。问是否能把每个点赋值以满足所有边的要求。

这题每个点有两个选择:0或1,所以二选一,并且点与点之间又有限制关系,于是可以2-SAT。

每个点拆成2个:2*i表示i号点放0,2*i+1表示i号点放1,那么根据逻辑关系即可构图。

 

void build()//对于两个不相容的点i,j 构图方式为:i->j'和j->i'
{
    init();
    int x,y,z;
    char s[5];
    for (int i = 0;i < m;i++)
    {
        scanf("%d%d%d%s",&x,&y,&z,s);
        //2*x:x位置放0 2*x+1:x位置放1
        if (strcmp(s,"AND") == 0) {
            if (z == 1) {
                addedge(2*x,2*x+1);
                addedge(2*y,2*y+1);
                addedge(2*x+1,2*y+1);
                addedge(2*y+1,2*x+1);
            }
            else {
                addedge(2*x+1,2*y);
                addedge(2*y+1,2*x);
            }
        }
        if (strcmp(s,"OR") == 0) {
            if (z == 1) {
                addedge(2*x,2*y+1);
                addedge(2*y,2*x+1);
            }
            else {
                addedge(2*x,2*y);
                addedge(2*y,2*x);
                addedge(2*x+1,2*x);
                addedge(2*y+1,2*y);
            }
        }
        if (strcmp(s,"XOR") == 0) {
            if (z == 1) {
                addedge(2*x+1,2*y);
                addedge(2*y,2*x+1);
                addedge(2*y+1,2*x);
                addedge(2*x,2*y+1);
            }
            else {
                addedge(2*x+1,2*y+1);
                addedge(2*y+1,2*x+1);
                addedge(2*x,2*y);
                addedge(2*y,2*x);
            }
        }
    }
}

 

 

[PKU3683]Priest John's Busiest Day

题意:有 n个婚礼,每个婚礼有起始时间Si,结束时间Ti,还有一个主持时间ti,ti必须安排在婚礼的开始或者结束,主持由祭祀来做,但是只有一个祭祀,所以各 个婚礼的主持时间不能重复,问你有没有可能正常的安排主持时间,不能输出no,能的话输出任意一个满足的方案,即每个婚礼的主持时间段。

这题因为限制了首做或者尾做,二选一,于是就很显然要2-SAT了,把每个Si,Ti看成一组,冲突就是对于不同的组i,j,分别考虑头尾是否冲突并连边即可。

 

void build()//对于两个不相容的点i,j 构图方式为:i->j'和j->i'
{
    init();
    int x,y,z;
    char s[5];
    for (int i = 0;i < m;i++)
    {
        scanf("%d%d%d%s",&x,&y,&z,s);
        //2*x:x位置放0 2*x+1:x位置放1
        if (strcmp(s,"AND") == 0) {
            if (z == 1) {
                addedge(2*x,2*x+1);
                addedge(2*y,2*y+1);
                addedge(2*x+1,2*y+1);
                addedge(2*y+1,2*x+1);
            }
            else {
                addedge(2*x+1,2*y);
                addedge(2*y+1,2*x);
            }
        }
        if (strcmp(s,"OR") == 0) {
            if (z == 1) {
                addedge(2*x,2*y+1);
                addedge(2*y,2*x+1);
            }
            else {
                addedge(2*x,2*y);
                addedge(2*y,2*x);
                addedge(2*x+1,2*x);
                addedge(2*y+1,2*y);
            }
        }
        if (strcmp(s,"XOR") == 0) {
            if (z == 1) {
                addedge(2*x+1,2*y);
                addedge(2*y,2*x+1);
                addedge(2*y+1,2*x);
                addedge(2*x,2*y+1);
            }
            else {
                addedge(2*x+1,2*y+1);
                addedge(2*y+1,2*x+1);
                addedge(2*x,2*y);
                addedge(2*y,2*x);
            }
        }
    }
}

 

 

 

posted @ 2012-10-28 09:25  芒果布丁  阅读(191)  评论(0编辑  收藏  举报