[关键字]:2-sat
[题目大意]:有n组钥匙,每组只能用一把;m扇门只能按顺序开,每个门需两把钥匙任选一把开,问罪多开几扇门。
//=====================================================================================================
[分析]:我一开始建图建错了,是把每扇门中的两把钥匙分别和与对方在同一组里的钥匙连边,结果过了样例。但后来看题解发现是对于这一题,有两类边:
1。有N对点 (A,B)表示钥匙A,B只能用一把,加点A',B',建边<A,B'>表示,用A钥匙,就不能用B钥匙,建边<B,A'>表示,用B钥匙,就不能用A钥匙
2。有M对点 (A,B)表示锁A,B至少得开一把,建边<A',B>,表示如果不开A锁,就必须开B锁,同样建边<B',A>
我之前错就是因为2-sat算法是依靠图的对称性来求解的,而我初始的建图方法没有构成对称性,所以不对。建好图后,二分答案并每次都重新建图并验证找到最优解。
[代码]:
View Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
#define MAXN 10000
struct rec
{
int x,y;
}p[MAXN],q[MAXN];
int n,m,sum,tot;
int dfn[MAXN],low[MAXN],belong[MAXN];
bool instack[MAXN];
vector<int> e[MAXN];
stack<int> s;
void tarjan(int u)
{
int i;
dfn[u]=low[u]=++tot;
s.push(u);
instack[u]=1;
int size=e[u].size();
for (i=0;i<size;i++)
{
int v=e[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])
{
sum++;
do{
i=s.top();
s.pop();
instack[i]=0;
belong[i]=sum;
}while (i!=u);
}
}
bool solveable(int d)
{
for (int i=0;i<4*n;i++) e[i].clear();
for (int i=0;i<n;i++)
{
e[q[i].x*2].push_back(q[i].y*2+1);
e[q[i].y*2].push_back(q[i].x*2+1);
}
for (int i=0;i<d;i++)
{
e[p[i].x*2+1].push_back(p[i].y*2);
e[p[i].y*2+1].push_back(p[i].x*2);
}
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(instack,0,sizeof(instack));
memset(belong,0,sizeof(belong));
sum=tot=0;
for (int i=0;i<4*n;i++)
if (!dfn[i]) tarjan(i);
/*for (int i=0;i<2*n;i++)
{
int size=e[i].size();
for (int j=0;j<n;j++)
printf("%d %d\n",i,j);
// printf("%d %d\n",i,belong[i]);
}*/
for (int i=0;i<2*n;i+=2)
if (belong[i]==belong[i+1]) return 0;
return 1;
}
int solve()
{
int l=0,r=m,mid;
while (l<r)
{
//printf("%d %d ",l,r);
mid=(l+r)/2+((l+r) & 1);
//printf("%d\n",mid);
if (solveable(mid)) l=mid; else r=mid-1;
}
return l;
}
int main()
{
while (scanf("%d%d",&n,&m),n || m)
{
for (int i=0;i<n;i++) scanf("%d%d",&q[i].x,&q[i].y);
for (int i=0;i<m;i++) scanf("%d%d",&p[i].x,&p[i].y);
//for (int i=0;i<2*n;i++) printf("%d %d\n",i,fright[i]);
printf("%d\n",solve());
}
return 0;
}