题意:给出n对钥匙,每对钥匙只能选其中一个,在给出每层门需要的两个钥匙,只要一个钥匙就能开门,问最多能到哪层。
思路:了解了2-SAT判定的问题之后主要就是建图的问题了,这里建图就是对于2*n个钥匙,分别分成a和a'两组,即选了比如a,b一组钥匙,选择了a则必须选择b',那么进行连边,而对于每层门,若该门能开,且选择了a',则b肯定需要选,那么a'和b连边。最后就是判定了,同样的 若a与a'在同一个连通分量中,表示无解。这题需要利用二分枚举答案m。
代码:
#include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<algorithm> #define MAXN 5005 #define MAXM 50005 #define inf 100000000 using namespace std; struct Edge { int v,next; }edge[MAXM]; int n,m,tot; int top,scnt,index; int low[MAXN],dfn[MAXN],instack[MAXN],head[MAXN]; int stack[MAXN],fa[MAXN],a[MAXN],b[MAXN],hash[MAXN]; int x[MAXN],y[MAXN]; void init() { top=scnt=index=tot=0; memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(instack,0,sizeof(instack)); } void addedge(int u,int v) { edge[tot].v=v; edge[tot].next=head[u]; head[u]=tot++; } void tarjan(int u) { low[u]=dfn[u]=++index; instack[u]=1; stack[++top]=u; int v; for(int i=head[u];i!=-1;i=edge[i].next) { v=edge[i].v; 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]) { scnt++; do { v=stack[top--]; instack[v]=0; fa[v]=scnt; }while(v!=u); } } bool check(int n) { for(int i=1;i<=n;i++) { if(fa[i]==fa[i+n]) return false; } return true; } void build(int bound) { init(); for(int i=1;i<=n;i++) { addedge(a[i],b[i]+2*n); addedge(b[i],a[i]+2*n); } for(int i=1;i<=bound;i++) { addedge(x[i]+2*n,y[i]); addedge(y[i]+2*n,x[i]); } for(int i=1;i<=2*n;i++) { if(!dfn[i])tarjan(i); } } void solve() { int ans=0; int low=0,high=m; while(low<=high) { int mid=(low+high)>>1; build(mid); if(check(2*n))ans=max(ans,mid),low=mid+1; else high=mid-1; } printf("%d ",ans); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { if(n==0&&m==0)break; for(int i=1;i<=n;i++) { scanf("%d%d",&a[i],&b[i]); a[i]++;b[i]++; } for(int i=1;i<=m;i++) { scanf("%d%d",&x[i],&y[i]); x[i]++;y[i]++; } solve(); } return 0; }