HDU_4115 Eliminate the Conflict 2-Sat

http://acm.hdu.edu.cn/showproblem.php?pid=4115

去年成都现场赛的一道题,那时候还不知道什么叫2-Sat ,今天终于1Y。

题意:

有两个人玩一个石头剪刀布的游戏,两个人连续玩N轮,给出其中

一个人的N轮出的情况和该人对另外一个人的一些限制条件,有两种限制:

每种限制表示为:(a,b,c) ,如果c==0 则表示该人对另外一个人的限制为第a

局和第b局出的应该一样,如果c==1表示不一样,问另外一个人是否有赢的

可能。

思路:

从表面上看,对于每一局,另外一个人有3中选择,似乎不能用2-Sat,但是

仔细想一下就会发现,其实3中选择中,只有两种是合法的,也就是说如果

选择另外一种的话,那个人就会输了,所以只要考虑两种不输的情况就可以了。

这样就可以按照那个人的序列确定出另外一个人的不输序列,并且没一轮只有

两种选择,这样就转化为标准的2-Sat问题了,然后就是建图,(a,b,c):

当c == 0 时, 要求a和b相同,枚举a的合法出牌,若果对于a的某种出牌,b对

应的是不合法出牌,也就说明a不能选择这种出法,这可以通过

加边(a , a')来实现。

当 c == 1时:要求a和b不相同,枚举a的合法出牌,如果对应的是b的不合法出

牌,这也就说明,如果a出这张牌,b可以随便出两张牌中的任意

一张,这种情况不能加边;如果对应的是合法边,那么这时候b

只有一种出牌情况,只要找出加边即可。

建完图之后就用tarjin缩点判断是否存在解即可。


代码:

#include<stdio.h>
#include<string.h>
int T,N,M;
int Bob[10010] ;
int Gr[10010*2], Gnext[10*10010] , Gv[10*10010] , Gc ;
int res[10010*2] ;
bool ok[10010][5] ;
int nn[10010][5] ;
const int MAXN = 20010 ;
int dfn[MAXN]  ,low[MAXN]  ,stack[MAXN]  ,belong[MAXN] ;
bool in[MAXN]  ;
int top ,idx , Bcnt ;

void tarjin(int u){
    int v ;
    low[u] = dfn[u] = ++idx ;
    stack[++top] = u ; in[u] = 1 ;
    for(int i=Gr[u];i!=-1;i=Gnext[i]){
        v = Gv[i] ;
        if( !dfn[v] ){
            tarjin(v) ;
            if( low[v] < low[u])
                low[u] = low[v] ;
        }
        else if( in[v] && dfn[v] < low[u])
            low[u] = dfn[v] ;
    }
    if( low[u] == dfn[u] ){
        Bcnt++ ;
        do{
            v = stack[top--] ; in[v] = 0 ;
            belong[v] = Bcnt ;
        }while(u != v) ;
    }
}

bool solve(){
    top = idx = Bcnt = 0 ;
    memset(dfn , 0 ,sizeof(dfn));
    memset(in , 0 ,sizeof(in));
    for(int i=1;i<=2*N;i++){
        if( !dfn[i] )   tarjin(i) ;
    }
    for(int i=1;i<=N;i++){
        if( belong[i] == belong[i+N] )  return false ;
    }
    return true ;
}
void add(int a, int b){
    Gv[Gc] = b ;
    Gnext[Gc] = Gr[a] ;
    Gr[a] = Gc++ ;
}
int main(){
    scanf("%d",&T);
    int cas = 0 ,a,b,c;
    int aa ,bb;
    while(T--){
        ++cas ;
        scanf("%d%d",&N,&M);
        memset(ok , 0, sizeof(ok));
        for(int i=1;i<=N;i++){
            scanf("%d",&Bob[i]);
            Bob[i]-- ;
            res[i] = Bob[i];
            res[i+N] = (Bob[i] + 1) % 3 ;
            ok[i][ res[i] ] = ok[i][ res[i+N] ] = 1 ;
            nn[i][ res[i] ] = i ;
            nn[i][ res[i+N] ] = i + N ;
        }

        memset(Gr, -1,sizeof(Gr)); Gc = 0 ;
        for(int i=1;i<=M;i++){
            scanf("%d%d%d",&a,&b,&c);
            if(c == 0){
                aa = res[a] ;
                bb = res[a+N] ;
                if( ok[b][aa]==0 ) add(a,a+N);
                else    add(a,nn[b][aa]) ;
                if( ok[b][bb] == 0)  add(a+N,a) ;
                else    add(a+N,nn[b][bb]);
                aa = res[b] ;
                bb = res[b+N] ;
                if( ok[a][aa] == 0) add(b,b+N);
                else                add(b,nn[a][aa]) ;
                if( ok[a][bb] == 0) add(b+N,b) ;
                else                add(b+N,nn[a][bb]) ;
            }
            else{
                aa = res[a] ;
                bb = res[a+N] ;
                if( ok[b][aa] == 1 ){
                for(int j=0;j<3;j++){
                    if(aa==j || ok[b][j]==0)    continue ;
                    add(a, nn[b][j] );
                }
                }
                if( ok[b][bb] == 1){
                for(int j=0;j<3;j++){
                    if(bb==j || ok[b][j]==0)    continue ;
                    add(a+N,nn[b][j]);
                }
                }
                aa = res[b] ;
                bb = res[b+N] ;
                if( ok[a][aa] == 1 ){
                for(int j=0;j<3;j++){
                    if(aa==j || ok[a][j]==0)    continue ;
                    add(b, nn[a][j] );
                }
                }
                if( ok[a][bb] == 1){
                for(int j=0;j<3;j++){
                    if(bb==j || ok[a][j]==0)    continue ;
                    add(b+N,nn[a][j]);
                }
                }
            }
        }
        printf("Case #%d: ",cas);
        if( solve() )   printf("yes\n");
        else            printf("no\n");
    }
    return 0 ;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值