POJ_2723 Get Luffy Out 2-Sat

http://poj.org/problem?id=2723

题意:

有2n把钥匙,分成2组,给你每组的钥匙信息,并且每组的钥匙只能用一个。

有m个门,每个门有2个锁,只要打开一个锁这个门就开了。(顺序遇见m个门)

问你最多能够打开多少个门。

思路:

因为门是按顺序打开的,所以用二分枚举能打开的门数,然后判断是否可行是比较好的选择。

建图主要从下面考虑: 对于第i扇门上的两把锁,要是两把锁同时打开,要么其中一把打开,

另外一把不能打开。即:(!a - > b) 和 ( !b -> a ) 。

代码:

#include<stdio.h>
#include<string.h>
const int MAXN = 1050*2 ;
int N , M ;
int x[1050] ,y[1050] ;
int L1[3000] , L2[3000];
int fer[MAXN] , nn[MAXN];
int Gv[MAXN*MAXN] , Gnext[MAXN*MAXN] , Gr[MAXN] , Gc ;
int dfn[MAXN] , low[MAXN] , stack[MAXN] , belong[MAXN] ;
bool in[MAXN] ;
int top, idx , Bcnt ;

void add(int  a, int b){
    Gv[Gc] = b ;
    Gnext[Gc] = Gr[a] ;
    Gr[a] = Gc++ ;
}
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(int m){
    memset(Gr, -1, sizeof(Gr)) ; Gc = 0 ;
    for(int i=1;i<=m;i++){
        int a = nn[fer[L1[i]]] ;
        int b = nn[L2[i]] ;
        add(a,b) ;
        a = nn[fer[L2[i]]] ;
        b = nn[L1[i]] ;
        add(a,b);
    }

    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 deal(){
    int l , h ,m ;
    l = 0 ; h = M ;
    while(l < h){
         m = (l + h + 1) >> 1 ;
         if( solve(m) ){
            l = m ;
         }
         else
            h = m - 1 ;
    }
    printf("%d\n",l);
}
int main(){
    while(scanf("%d%d",&N,&M) == 2){
        if(0==N && M==0)    break ;
        for(int i=1;i<=N;i++){
            scanf("%d%d",&x[i] ,&y[i]);
            fer[ x[i] ] = y[i] ;
            fer[ y[i] ] = x[i] ;
            nn[ x[i] ] = i ;
            nn[ y[i] ] = i + N ;
        }
        for(int i=1;i<=M;i++){
            scanf("%d%d",&L1[i] , &L2[i]);
        }
        deal() ;
    }

    return 0 ;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值