题意: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+二分答案)
最新推荐文章于 2021-10-10 13:19:19 发布