POJ 2723 Get Luffy Out(2-SAT)
【题目链接】 http://poj.org/problem?id=2723
【题目大意】
给出一些钥匙和M扇有顺序的门,每扇门可以用两种钥匙打开,
每两把钥匙被绑在一起,绑在一起的钥匙只有其中一把可以使用,
问最多能按顺序打开几扇门。
【题解】
因为门是按顺序的,因此能打开的门是单调,
首先我们二分这个答案,判定是否可行,
将二分得到的答案之前的门按两个钥匙孔拆分成两个点,
两个绑在一起的钥匙属于对立面,而门的两个钥匙孔是OR关系
A与B之间的OR关系,我们可以将其拆分为!A->B AND !B->A,
m个关系联立,就可以用2-SAT验证是否可行,
【代码】
#include <cstdio> #include <algorithm> #include <vector> #include <cstring> using namespace std; const int MAX_V=10000; int V; //顶点数 vector<int> G[MAX_V]; //图的邻接表表示 vector<int> rG[MAX_V]; //反向图 vector<int> vs; //后序遍历 bool used[MAX_V]; int cmp[MAX_V]; //所属强连通分量的拓扑序 void add_edge(int from,int to){ G[from].push_back(to); rG[to].push_back(from); } void dfs(int v){ used[v]=1; for(int i=0;i<G[v].size();i++){ if(!used[G[v][i]])dfs(G[v][i]); }vs.push_back(v); } void rdfs(int v,int k){ used[v]=1; cmp[v]=k; for(int i=0;i<rG[v].size();i++){ if(!used[rG[v][i]])rdfs(rG[v][i],k); } } const int MAX_N=1<<10; const int MAX_M=1<<11; int N,M,x,y; int door[MAX_M][2],key[MAX_N]; int scc(){ memset(used,0,sizeof(used)); vs.clear(); for(int v=0;v<V;v++){if(!used[v])dfs(v);} memset(used,0,sizeof(used)); int k=0; for(int i=vs.size()-1;i>=0;i--){ if(!used[vs[i]])rdfs(vs[i],k++); }return k; } bool check(int x){ V=2*N; for(int i=0;i<V;i++){G[i].clear();rG[i].clear();} for(int i=0;i<x;i++){ add_edge(key[door[i][0]],door[i][1]); add_edge(key[door[i][1]],door[i][0]); }scc(); for(int i=0;i<V;i++){ if(cmp[i]==cmp[key[i]])return 0; }return 1; } int solve(){ int l=0,r=M,ans=0; while(l<=r){ int mid=(l+r)>>1; if(check(mid))l=mid+1,ans=mid; else r=mid-1; }return ans; } int main(){ while(~scanf("%d%d",&N,&M),N+M){ for(int i=0;i<N;i++){ scanf("%d%d",&x,&y); key[x]=y; key[y]=x; }for(int i=0;i<M;i++)scanf("%d%d",door[i],door[i]+1); printf("%d\n",solve()); }return 0; }
愿你出走半生,归来仍是少年