有m层楼,每层楼通往下一层的门有两个钥匙孔,需要对应编号的钥匙,只需打开一个钥匙孔,门即可打开。有2×n把钥匙,把钥匙分成n组,每组2把。钥匙互不相同,编号为0-2×n-1,每组钥匙其中一把被使用后,两把钥匙都会消失。问:最多到达那一层楼。
输入:
n m
以下n行是n组钥匙里每组钥匙的组成编号
以下m行是1-m层的门上的钥匙孔需要的钥匙。注:不同门可能需要同种钥匙,一扇门也可能2个钥匙孔都需要同种钥匙。
输出:
直接输出答案
思路:2-sat问题,对于每一扇门可以视为一个限制条件。如一扇门需要的钥匙是a或c,而a,b为一组钥匙,c,d为一组钥匙,则,若在前面选择了b钥匙,则这里必须选c钥匙,故有b->c边,同理,有d->a边。二分最远抵达层数,将二分到的层数前面的门的边都加上,然后求强连通分量。若存在一组钥匙落在同一个强连通分量里,则无解,反之,有解。
//time:32ms
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<vector>
#define BUG printf("here\n");
using namespace std;
const int N=3010;
int pre[N],low[N],f[N],scc[N];
int n,m,cnt,num;
int x[N],y[N];
vector<int> G[N];
stack<int> s;
void DFS(int u)
{
pre[u]=low[u]=++num;
s.push(u);
int v;
for(int i=0;i<G[u].size();++i) {
v=G[u][i];
if(!pre[v]) {
DFS(v);
low[u]=min(low[u],low[v]);
}
else if(!scc[v])low[u]=min(low[u],pre[v]);
}
if(pre[u]==low[u]) {
int tmp=-1;
++cnt;
while(tmp!=u) {
tmp=s.top();
s.pop();
scc[tmp]=cnt;
}
}
}
void Tarjin()
{
cnt=0,num=0; //num是时间戳,cnt是强连通分量的数目
while(!s.empty()) s.pop();
memset(pre,0,sizeof pre);
memset(scc,0,sizeof scc);
for(int i=0;i<n;++i)
if(!pre[i])
DFS(i);
}
bool solve()
{
Tarjin();
/*for(int i=0;i<n;++i)
printf("%d ",scc[i]);
printf("\n");*/
for(int i=0;i<n;++i)
if(scc[i]==scc[f[i]]) //若存在一组钥匙落在同一个强连通分量里,则无解
return false;
return true;
}
bool one(int a)
{
for(int i=0;i<n;++i)
G[i].clear();
for(int i=0;i<a;++i) {
G[f[x[i]]].push_back(y[i]);
G[f[y[i]]].push_back(x[i]);
}
return solve();
}
int main()
{
int L,R,mid;
int p,q;
while(scanf("%d%d",&n,&m)&&(n|m)) {
for(int i=0;i<n;++i){
scanf("%d%d",&p,&q);
f[p]=q;
f[q]=p;
}
//BUG;
for(int i=0;i<m;++i) {
scanf("%d%d",x+i,y+i);
}
//BUG;
n<<=1;
L=0,R=m;
while(L<R) {
mid=L+(R-L+1)/2;
if(one(mid)) L=mid;
else R=mid-1;
}
printf("%d\n",L);
}
return 0;
}