poj 2723(2-sat+二分答案)

题意:m个门,每个门上有两把锁,打开一个就可以通过 
           2n个钥匙,每两个绑在一起,只能选用一个 ,选了一个,另一个就被废弃。
           问最多可以通过几扇门? 
 
2-sat问题关键在建图,2-sat对每个事物都有两个选项 ,选和不选.
 
可以这么建: 
每把钥匙有两个状态(用或不用),把这作为2-sat的两个选项 
然后是加条件,a、b绑在一起,则选a就不选b,选b就不选a,建边a->!b,b->!a 
c、d在同一个门上,则不开c就开d,不开d就开c,建边!c->d,!d->c 
 
然后二分答案都可以了 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;


const int NN=1300*4;
int n,m;
int dfn[NN];
int low[NN];
int tmp;
int cnt;
int fa[NN];
vector<int>mp[NN];
int vis[NN];
stack<int>mystack;


int key1[NN],key2[NN];
int door1[NN],door2[NN];


void init()
{
    for(int i=0;i<n*2*2+10;i++)
      mp[i].clear();
    tmp=0;
    cnt=0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(fa,0,sizeof(fa));
    memset(low,0,sizeof(low));
}


void tarjan(int u)//求强连通
{
    vis[u]=1;
    dfn[u]=low[u]=tmp++;
    mystack.push(u);
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[v],low[u]);
        }
        else if(vis[v])
        low[u]=min(dfn[v],low[u]);
    }
    if(dfn[u]==low[u])
    {
      cnt++;
      int t;
      do
      {
        t=mystack.top();
        mystack.pop();
        vis[t]=0;
        fa[t]=cnt;
      }while(t!=u);
    }
}


void built(int to)
{
  init();
  for(int i=1;i<=n;i++)
  {
      mp[key1[i]*2].push_back(key2[i]*2+1);
      mp[key2[i]*2].push_back(key1[i]*2+1);
  }
  for(int i=1;i<=to;i++)
  {
      mp[door1[i]*2+1].push_back(door2[i]*2);
      mp[door2[i]*2+1].push_back(door1[i]*2);
  }
}


int solve()
{
    for(int i=0;i<2*n*2;i++)
    {
        if(!dfn[i])
        tarjan(i);
    }
    for(int i=0;i<2*n;i++)
    {
        if(fa[i*2]==fa[i*2+1])
        return 0;
    }
    return 1;
}


int main()
{
   while(cin>>n>>m,n,m)
   {
    for(int i=1;i<=n;i++)
    cin>>key1[i]>>key2[i];
    for(int j=1;j<=m;j++)
    cin>>door1[j]>>door2[j];
    int L=0;int R=m;
    int mid;
    int ans=0;
    while(L<=R)
    {
        mid=(L+R)/2;
        //cout<<mid<<endl;
        built(mid);
        if(solve())
         ans=max(ans,mid),  L=mid+1;
        else R=mid-1;
    }
    cout<<ans<<endl;
  }


    return 0;
}

你可能感兴趣的:(poj 2723(2-sat+二分答案))