HDU 3062 & HDU 1824 (2-sat入门题)

本文介绍了解决2-SAT问题的方法,通过构造图和使用Tarjan算法进行强连通分量分析来判断是否存在逻辑上不矛盾的解。提供了两个具体的编程实例,详细展示了如何实现这一过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《由对称性解2-SAT问题》:http://wenku.baidu.com/view/afd6c436a32d7375a41780f2.html

所谓2-sat问题,就是在满足一定逻辑条件下,是否存在可行解的问题,这里的“可行”指的是在逻辑上不出现矛盾。具体参见上述ppt,确实是非常不错的讲解。

问题类型:

通常来说,一般是有若干个组,每一个组有两个元素,A和A',二者只能选择一个。同时还有一些逻辑关系,比如A和B不能同时存在,B不能和C存在等。问题是,是否存在从每个组中选出一个元素,使得所选的元素满足两两相容。

问题解决方法:

构图:比如A和B不能存在,B和C'不能同时存在,那么选择A的话,则必选B',同时,若选了B,则必定要选A'。同理若选择了B,则必须选C,选则了C'则必须选B'。可以得到其中的对称关系。这里的构图方式是,对于A和B不能共存,则A-B',B-A'...

缩点:对于构成的图,用强连通的tarjan算法进行缩点,如果对于任意X和X',如果他们在同一个环中,说明没有解(因为在同一个组中的元素,二者只能出现一个)。

输出可行解:若存在解,将缩点后的图上的边反向,然后进行拓扑排序。

 

这两道题不需要输出可行解,故直接构图缩点判断即可:

HDU3602:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
#define SIZE 2010

using namespace std;

struct node
{
    int to,next;
}edge[SIZE*SIZE];
int head[SIZE],idx;
int n,m;
int dfsn[SIZE],low[SIZE],tim;
int be[SIZE],num;
stack <int> sta;
bool instack[SIZE];

void addnode(int from,int to)
{
    edge[idx].to = to;
    edge[idx].next = head[from];
    head[from] = idx++;
}

void tarjan(int cur)
{
    dfsn[cur] = low[cur] = ++tim;
    sta.push(cur);
    instack[cur] = true;
    for(int i=head[cur]; i!=-1; i=edge[i].next)
    {
        int to = edge[i].to;
        if(!dfsn[to])
        {
            tarjan(to);
            low[cur] = min(low[cur],low[to]);
        }
        else if(instack[to])
            low[cur] = min(low[cur],dfsn[to]);
    }
    if(dfsn[cur] == low[cur])
    {
        int tem = -1;
        ++num;
        while(tem != cur)
        {
            tem = sta.top();
            sta.pop();
            instack[tem] = false;
            be[tem] = num;
        }
    }
}

void solve()
{
    memset(dfsn,0,sizeof(dfsn));
    memset(low,0,sizeof(low));
    memset(instack,0,sizeof(instack));
    memset(be,0,sizeof(be));
    while(!sta.empty()) sta.pop();
    tim = num = 0;
    for(int i=0; i<2*n; i++)
        if(!dfsn[i])
            tarjan(i);
    bool ans = true;
    for(int i=0; i<2*n; i+=2)
        if(be[i] == be[i+n]){
            ans = false;
            break;
        }
    if(ans) puts("YES");
    else puts("NO");
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        idx = 0;
        memset(head,-1,sizeof(head));
        int x,y,sex1,sex2;
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d%d",&x,&y,&sex1,&sex2);
            if(sex1 && sex2)
            {
                addnode(x+n,y);
                addnode(y+n,x);
            }
            if(!sex1 && sex2)
            {
                addnode(x,y);
                addnode(y+n,x+n);
            }
            if(sex1 && !sex2)
            {
                addnode(x+n,y+n);
                addnode(y,x);
            }
            if(!sex1 && !sex2)
            {
                addnode(x,y+n);
                addnode(y,x+n);
            }
        }
        solve();
    }
    return 0;
}

 

HDU1824:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
#define SIZE 2020

using namespace std;

struct node
{
    int to,next;
}edge[SIZE*10];
int head[SIZE],idx;
int n,m;
int pos[SIZE];
int dfsn[SIZE],low[SIZE],tim;
int be[SIZE],num;
bool instack[SIZE];
stack <int> sta;

void addnode(int from,int to)
{
    edge[idx].to = to;
    edge[idx].next = head[from];
    head[from] = idx++;
}

void tarjan(int cur)
{
    dfsn[cur] = low[cur] = ++tim;
    sta.push(cur);
    instack[cur] = true;
    for(int i=head[cur]; i!=-1; i=edge[i].next)
    {
        int to = edge[i].to;
        if(!dfsn[to])
        {
            tarjan(to);
            low[cur] = min(low[cur],low[to]);
        }
        else if(instack[to])
            low[cur] = min(low[cur],dfsn[to]);
    }
    if(dfsn[cur] == low[cur])
    {
        int tem = -1;
        ++num;
        while(tem != cur)
        {
            tem = sta.top();
            sta.pop();
            instack[tem] = false;
            be[tem] = num;
        }
    }
}

void solve()
{
    memset(dfsn,0,sizeof(dfsn));
    memset(low,0,sizeof(low));
    memset(be,0,sizeof(be));
    memset(instack,0,sizeof(instack));
    while(!sta.empty()) sta.pop();
    tim = num =  0;
    for(int i=0; i<2*n; i++)
        if(!dfsn[i])
            tarjan(i);
    bool ans = true;
    for(int i=0; i<2*n; i++)
        if(be[i] == be[i+n]){
            ans = false;
            break;
        }
    if(ans) puts("yes");
    else puts("no");
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        int x,y,z;
        for(int i=0; i<n; i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            pos[x] = i;
            pos[y] = pos[z] = i+n;
        }
        idx = 0;
        memset(head,-1,sizeof(head));
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&x,&y);
            if(pos[y] >= n)
                addnode(pos[x],pos[y]-n);
            else
                addnode(pos[x],pos[y]+n);
            if(pos[x] >= n)
                addnode(pos[y],pos[x]-n);
            else
                addnode(pos[y],pos[x]+n);
        }
        solve();
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值