HDU 4115 Eliminate the Conflict(2-SAT)

9 篇文章 0 订阅

Description
Bob和Alice玩剪刀石头布,一个玩n轮,Alice已经知道了Bob每次要出什么,1代表剪刀,2代表石头,3代表布,然后Bob对Alice作出了一些限制:给m行,每行是a b k,如果k是0,表示Alice第a次和b次出的拳必须相同,如果k是1,表示Alice第a次和b次出的拳必须不相同。一但Alice破坏了这个限制规则,或者输了一局,那么Alice就彻底输了。问Alice可不可能赢?
Input
第一行为用例组数t,每组用例第一行为两个整数n和m表示局数和限制数,第二行为n个整数分别表示bob这n局要出什么,最后m行每行一次限制
Output
对于每组用例,如果alice可以赢则输出yes,否则输出no
Sample Input
2
3 3
1 1 1
1 2 1
1 3 1
2 3 1
5 5
1 2 3 2 1
1 2 1
1 3 1
1 4 1
1 5 1
2 3 0
Sample Output
Case #1: no
Case #2: yes
Solution
因为Alice一次都不能输,所以根据Bob出的拳,Alice只可以赢或者平局,即每次有两种选择,是2-SAT模型。然后会有一些矛盾对,假设第a次可以出a1,a2, 第b次可以出b1和b2
如果第a次和b次要求相同, 但是a1和b1不同,说明这个矛盾,建立连接 a1->b2, b1->a2
同理,第a次和b次要求不相同,但是a1和b1相同,说明这个矛盾,建立链接a1->b2, b1->a2
以此类推可以构出关系图,接下来只需用tarjan算法求强连通分量并判断是否存在使得布尔公式值为真的一组布尔变量赋值即可
Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
#define maxn 11111
vector<int>g[maxn];  
stack<int>st;  
int n,m,scc,index;  
int low[maxn],dfn[maxn],instack[maxn],fa[maxn];  
int bob,alice[maxn][2];
void init()//初始化 
{  
    scc=index=0;  
    while(!st.empty())st.pop();  
    for(int i=0;i<maxn;i++)g[i].clear();  
    memset(dfn,0,sizeof(dfn));  
    memset(instack,0,sizeof(instack));  
    memset(low,0,sizeof(low));
}  
void tarjan(int u)//求强联通分量  
{  
    dfn[u]=low[u]=++index;  
    instack[u]=1;  
    st.push(u);  
    int v,size=g[u].size();  
    for(int i=0;i<size;i++)  
    {  
        v=g[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])  
    {  
        scc++;  
        do  
        {  
            v=st.top();  
            st.pop();  
            fa[v]=scc;  
            instack[v]=0;  
        }while(v!=u);  
    }  
}  
bool check()//判断可行性 
{
    for(int i=0;i<2*n;i++)//求强联通分量 
        if(!dfn[i])
            tarjan(i);
    for(int i=0;i<2*n;i+=2)
        if(fa[i]==fa[i+1])
            return false;
    return true;
}
int main()
{
    int t;
    scanf("%d",&t);
    int res=1;
    while(t--)
    {
        init();//初始化 
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&bob);
            bob--;
            alice[i][0]=bob;//平局 
            alice[i][1]=(bob+1)%3;//赢局 
        }
        for(int i=0;i<m;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            a--;b--;
            if(c)//两次不同 
            {
                if(alice[a][0]==alice[b][0])
                {
                    g[2*a].push_back(2*b+1);
                    g[2*b].push_back(2*a+1);
                }
                if(alice[a][0]==alice[b][1])
                {
                    g[2*a].push_back(2*b);
                    g[2*b+1].push_back(2*a+1);
                }
                if(alice[a][1]==alice[b][0])
                {
                    g[2*a+1].push_back(2*b+1);
                    g[2*b].push_back(2*a);
                }
                if(alice[a][1]==alice[b][1])
                {
                    g[2*a+1].push_back(2*b);
                    g[2*b+1].push_back(2*a);
                }
            }
            else//两次相同 
            {
                if(alice[a][0]!=alice[b][0])
                {
                    g[2*a].push_back(2*b+1);
                    g[2*b].push_back(2*a+1);
                }
                if(alice[a][0]!=alice[b][1])
                {
                    g[2*a].push_back(2*b);
                    g[2*b+1].push_back(2*a+1);
                }
                if(alice[a][1]!=alice[b][0])
                {
                    g[2*a+1].push_back(2*b+1);
                    g[2*b].push_back(2*a);
                }
                if(alice[a][1]!=alice[b][1])
                {
                    g[2*a+1].push_back(2*b);
                    g[2*b+1].push_back(2*a);
                }
            } 
        }
        printf("Case #%d: ",res++);
        if(check())
            printf("yes\n");
        else
            printf("no\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值