有m层门,我们在最外层,我们要一层一层的进,每一层上有两把锁,我们只要开启其中的一把们就会开,我们有n组钥匙,每组两把,我们只能用其中的一把,用完后第二把瞬间就会消失,问你最多能开到多少层们。
思路:
果断二分+2sat,现在我们来看下怎么建边,首先我们把每把钥匙用看成a,不用看成~a ,对于没一组钥匙,我们不能同时选择两个,所以有 x ->~y ,y -> ~x,对于门,我们每次至少选择开一个,所以有 ~x -> y ,~y -> x,就这样二分每次重新建图就行了,顺便说下POJ2723 ,跟这个题目几乎差不多,但是唯一的区别就是那个题目每组钥匙不会重复,这个有可能是同一把钥匙属于多个组,如果用这个题目的代码直接去交POJ2723,直接就可以AC了,钥匙反过来就不一定了,因为那个题目既然说是一把钥匙最多只出现在一组,那么就没有必要把每把钥匙拆成a 和 ~a ,而是把每组的钥匙拆成 a ~a,这样就节省了点数和时间,同时也没有必要建 x ->~y y->~x,这样也节省的边,如果是那么做的,那么到这个题目上就WA了,所以我说这个代码粘到那个代码上肯定AC,反过来就不一定了。
#include<stdio.h> #include<string.h> #include<stack> #define N_node 5000 #define N_edge 50000 using namespace std; typedef struct { int to ,next; }STAR; STAR E1[N_edge] ,E2[N_edge]; int list1[N_node] ,list2[N_node] ,tot; int Belong[N_node] ,cnt; int mark[N_node]; int D[N_node][2] ,A[N_node][2]; int id[N_node]; stack<int>st; void add(int a ,int b) { E1[++tot].to = b; E1[tot].next = list1[a]; list1[a] = tot; a = a + b ,b = a - b ,a = a - b; E2[tot].to = b; E2[tot].next = list2[a]; list2[a] = tot; } void DFS1(int s) { mark[s] = 1; for(int k = list1[s] ;k ;k = E1[k].next) if(!mark[E1[k].to]) DFS1(E1[k].to); st.push(s); } void DFS2(int s) { mark[s] = 1; Belong[s] = cnt; for(int k = list2[s] ;k ;k = E2[k].next) if(!mark[E2[k].to]) DFS2(E2[k].to); } bool ok(int mid ,int n) { memset(list1 ,0 ,sizeof(list1)); memset(list2 ,0 ,sizeof(list2)); tot = 1; for(int i = 1 ; i<= n/2 ;i ++) { int x = A[i][0] * 2 ,xx = A[i][0] * 2 + 1; int y = A[i][1] * 2 ,yy = A[i][1] * 2 + 1; add(x ,yy) ,add(y ,xx); } for(int i = 1 ;i <= mid ;i ++) { int x = D[i][0] * 2 ,xx = D[i][0] * 2 + 1; int y = D[i][1] * 2 ,yy = D[i][1] * 2 + 1; add(xx ,y) ,add(yy ,x); } memset(mark ,0 ,sizeof(mark)); while(!st.empty()) st.pop(); for(int i = 0 ;i < n * 2 ;i ++) if(!mark[i]) DFS1(i); memset(mark ,0 ,sizeof(mark)); cnt = 0; while(!st.empty()) { int xin = st.top(); st.pop(); if(mark[xin]) continue; cnt ++; DFS2(xin); } int mk = 0; for(int i = 0 ;i < n * 2 && !mk;i += 2) if(Belong[i] == Belong[i^1]) mk = 1; return !mk; } int main () { int i ,n ,m ,a ,b; while(~scanf("%d %d" ,&n ,&m) && n + m) { for(i = 1 ;i <= n ;i ++) { scanf("%d %d" ,&a ,&b); A[i][0] = a ,A[i][1] = b; } for(i = 1 ;i <= m ;i ++) scanf("%d %d" ,&D[i][0] ,&D[i][1]); int low ,up , mid ,ans = 0; low = 0 ,up = m ,n *= 2; while(low <= up) { mid = (low + up) >> 1; if(ok(mid ,n)) ans = mid ,low = mid + 1; else up = mid - 1; } printf("%d " ,ans); } return 0; }