HDU 3062 Party(2-sat题模板+tarjan )

题目:

有n对夫妻被邀请参加一个聚会,因为场地的问题,每对夫妻中只有1人可以列席。在2n 个人中,某些人之间有着很大的矛盾(当然夫妻之间是没有矛盾的),有矛盾的2个人是不会同时出现在聚会上的。有没有可能会有n 个人同时列席?

Input

n: 表示有n对夫妻被邀请 (n<= 1000)
m: 表示有m 对矛盾关系 ( m < (n - 1) * (n -1))

在接下来的m行中,每行会有4个数字,分别是 A1,A2,C1,C2
A1,A2分别表示是夫妻的编号
C1,C2 表示是妻子还是丈夫 ,0表示妻子 ,1是丈夫
夫妻编号从 0 到 n -1

Output

如果存在一种情况 则输出YES
否则输出 NO

Sample Input

2
1
0 1 1 1

Sample Output

YES

分析

2-sat详解:https://blog.csdn.net/zeng_jun_yv/article/details/105316871
一道2—SAT的模板题,其实就是建立一个无向图,然后求强连通分量,再看看同一对夫妻是不是在同一个强连通分量里。由对称性解2-SAT问题

AC代码:

/*由对称性解2-SAT问题*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
#include<algorithm>
using namespace std;
const int N = 2010;
vector<int> vec[N];
int n, m, id, cnt;
int dfn[N], vis[N], low[N], belong[N];
stack<int> s;
void init()
{
    memset(vis,0,sizeof(vis));
    memset(dfn,-1,sizeof(dfn));
    memset(low,-1,sizeof(low));
    memset(belong,-1,sizeof(belong));
    id = cnt = 0;
    while(!s.empty())
        s.pop();
    for(int i = 0; i < 2*n; i++)
        vec[i].clear();
}
void tarjan(int u)
{
    dfn[u] = low[u] = id++;
    vis[u] = 1;
    int sz = vec[u].size();
    s.push(u);
    for(int i = 0; i < sz; i++)
    {
        int v = vec[u][i];
        if(dfn[v] == -1)
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(vis[v] == 1)
        {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if(low[u] == dfn[u])
    {
        cnt++;
        while(!s.empty())
        {
            int temp = s.top();
            s.pop();
            vis[temp]=0;
            belong[temp]=cnt;
            if(temp == u)
                break;
        }
    }
}
int main()
{
    int a,b,c,d;
    while(~scanf("%d%d", &n, &m))
    {
        init();
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d%d", &a, &b, &c, &d);
            vec[2*a+c].push_back(2*b+1-d);
            vec[2*b+d].push_back(2*a+1-c);
        }
        for(int i = 0; i < 2*n; i++)
        {
            if(dfn[i] == -1)
                tarjan(i);
        }
        bool flag = true;
        for(int i  = 0; i < n; i++)
        {
            if( belong[2*i] == belong[2*i+1] )
            {
                flag=false;
                break;
            }
        }
        puts(flag?"YES":"NO");
    }
    return 0;
}

Tarjan+缩点+拓扑+染色

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std ;
#define M 4000017
#define N 100017
//a<<1 和 (a<<1) + 1。a<<1表示妻子,(a<<1) + 1表示丈夫
//连接某边是为了推出矛盾。x->y表示选x则必须选y
//注意方向
struct node
{
    int s, t;
    int nxt;
} e[M];
int n, m;
int idx, ans, tp, cont;
int dfn[N],vis[N],low[N],head[N],st[N],belong[N];

void add(int s,int t)
{
    e[cont].s = s;
    e[cont].t = t;
    e[cont].nxt = head[s];
    head[s] = cont++;
}
void build_grap(int a, int b, int c, int d)//建图
{
    if(c==0 && d==0)//两个妻子
    {
        add(a<<1, (b<<1)+1);
        add(b<<1, (a<<1) + 1);
    }
    else if(c==0 && d==1)//妻子和丈夫
    {
        add((a<<1), (b<<1));
        add((b<<1)+1, (a<<1)+1);
    }
    else if(c==1 && d==0)//丈夫和妻子
    {
        add((a<<1) + 1, (b<<1) + 1);
        add(b<<1, a<<1);
    }
    else if(c==1 && d==1)//两个丈夫有矛盾
    {
        add((a<<1)+1, b<<1);
        add((b<<1)+1, a<<1);
    }
}
void tarjan(int u)
{
    dfn[u] = low[u] = ++idx;
    vis[u] = 1;
    st[++tp] = u;
    int v ;
    for(int i = head[u]; i != -1; i = e[i].nxt)
    {
        v = e[i].t ;
        if(!dfn[v])
        {
            tarjan(v) ;
            low[u] = min(low[u],low[v]);
        }
        else if(vis[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        ans++;
        while(1)
        {
            v = st[tp--];
            vis[v] = 0;
            belong[v] = ans;
            if(v == u)
                break;
        }
    }
}
bool _2sat()
{
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    idx = tp = ans = 0;
    for(int i = 0; i < 2*n; i++)
        if(!dfn[i])
            tarjan(i) ;
    for(int i = 0; i < n; i++)
        if(belong[2*i]==belong[(2*i)^1])//矛盾
            return false ;
    return true;
}

int main()
{
    int a, b, c, d;
    while(~scanf("%d%d",&n,&m))
    {
        cont = 0;
        memset(head,-1,sizeof(head));
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d%d",&a,&b,&c,&d);
            //int u, v;
            //u = a*2+c; v = b*2+d;
            //add(u, v^1); add(v, u^1);
            build_grap(a, b, c, d);
        }
        if(_2sat())
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0 ;
}
/**思路:每对夫妻代表图中一个结点,只有 1、0 两种选择,对于有矛盾的夫妻对,使其不列席,让无矛盾夫妻对的列席即可
对于 m 矛盾关系,设 a、b 两对夫妇存在矛盾:
若第 a 对的妻子与第 b 对的妻子有矛盾(a b 0 0)
则 a 的妻子去了 b 的丈夫必须去,b 的妻子去了 a 的丈夫必须去:<a,0,b,1>、<b,0,a,1>,添边:<a+n,b>,<b+n,a>
若第 a 对的妻子与第 b 对的丈夫有矛盾(a b 0 1)
则 a 的妻子去了 b 的妻子必须去,b 的丈夫去了 a 的丈夫必须去:<a,0,b,0>、<b,1,a,1>,添边:<a+n,b+n>,<b,a>
若第 a 对的丈夫与第 b 对的妻子有矛盾(a b 1 0)
则 a 的丈夫去了 b 的丈夫必须去,b 的妻子去了 a 的妻子必须去:<a,1,b,1>、<b,0,a,0,>,添边:<a,b>,<b+n,a+n>
若第 a 对的丈夫与第 b 对的丈夫有矛盾(a b 1 1)
则 a 的丈夫去了 b 的妻子必须去,b 的丈夫去了 a 的妻子必须去:<a,1,b,0>、<b,1,a,0>,添边:<a,b+n>,<b,a+n>*/
#include<iostream>
#include<cstdio>
#include<stack>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6+10;
const int dx[] = {-1,1,0,0,-1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;
struct node
{
    int to,next;
} node[N*2];
int head[N],tot;
int n,m;
int dfn[N],low[N];
bool vis[N];//标记数组
int scc[N];//记录结点i属于哪个强连通分量
int block_cnt;//时间戳
int sig;//记录强连通分量个数
stack<int> S;
void init()
{
    tot=0;
    sig=0;
    block_cnt=0;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(scc,0,sizeof(scc));
}
void addnode(int from,int to)
{
    node[++tot].to=to;
    node[tot].next=head[from];
    head[from]=tot;
}
void Tarjan(int x)
{
    vis[x]=true;
    dfn[x]=low[x]=++block_cnt;//每找到一个新点,纪录当前节点的时间戳
    S.push(x);//当前结点入栈

    for(int i=head[x]; i!=-1; i=node[i].next)   //遍历整个栈
    {
        int y=node[i].to;//当前结点的下一结点
        if(!dfn[y])
        {
            Tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(vis[y])
            low[x]=min(low[x],dfn[y]);
    }

    if(dfn[x]==low[x])   //满足强连通分量要求
    {
        sig++;//记录强连通分量个数

        while(true)   //记录元素属于第几个强连通分量
        {
            int temp=S.top();
            S.pop();
            vis[temp]=false;
            scc[temp]=sig;
            if(temp==x)
                break;
        }
    }
}
bool twoSAT()
{
    for(int i=1; i<=2*n; i++) //找强连通分量
        if(!dfn[i])
            Tarjan(i);
    for(int i=1; i<=n; i++)
        if(scc[i]==scc[i+n])//条件a与!a属于同一连通分量,无解
            return false;
    return true;
}
int main()
{
    while( scanf("%d%d",&n,&m)!=EOF&&(n+m))
    {
        init();
        while(m--)
        {
            int x,y,xVal,yVal;
            scanf("%d%d%d%d",&x,&y,&xVal,&yVal);
            x++;
            y++;
            if(xVal==0&&yVal==0) //x为0或y为0
            {
                addnode(x+n,y);//x为0,y为1
                addnode(y+n,x);//y为0,x为1
            }
            else if(xVal==0&&yVal==1) //x为0或y为1
            {
                addnode(x+n,y+n);//x为0,y为0
                addnode(y,x);//y为1,x为1
            }
            else if(xVal==1&&yVal==0) //x为1或y为0
            {
                addnode(x,y);//x为1,y为1
                addnode(y+n,x+n);//y为0,x为0
            }
            else if(xVal==1&&yVal==1) //x为1或y为1
            {
                addnode(x,y+n);//x为1,y为0
                addnode(y,x+n);//y为1,x为0
            }
        }

        bool flag=twoSAT();
        if(!flag)
            printf("NO\n");
        else
            printf("YES\n");
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值