POJ 2723 2SAT

题意:

有2n把钥匙,每2个一组,给你每组的钥匙信息,并且每组的钥匙只能用一个。有m个门,每个门有2个锁,只要打开一个锁这个门就开了。问你最多能够打开多少个门。

PS:打开门必须是有序的,即你想要打开i就必须之前已经打开了i-1

分析:

不知道我这样做的人多不多。

由于有序性,所以二份答案是必须的

我是把一个钥匙a(不是一组钥匙)拆成两个点a0和a1,分别表示没有取过和取过,对于和他同组的钥匙b,连两条边,a1-->b0和b1-->a0,表示取了a钥匙就不能再取b钥匙

对于每层的两把锁,c和d,连c0-->d1,d0-->c1,表示c钥匙不取则d钥匙必须取,d钥匙不取则c钥匙必须取,也就是说,必须要有一个钥匙来开这个锁

View Code
 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <iostream>
 5 using namespace std;
 6 const int N=(1<<13)+100000;
 7 const int M=(1<<25)+1000000;
 8 int head[N],next[M],to[M],dir[N],ans,n,m,cnt,dfn[N],low[N],belong[N],mid,stack[N],p,divg,t,do1[M],do2[M];
 9 bool fg[N];
10 void read()
11 {
12     for(int i=1,a,b;i<=n;i++)
13     {
14         scanf("%d%d",&a,&b),a++,b++;
15         dir[a]=b,dir[b]=a;
16     }
17     for(int i=1;i<=m;i++) scanf("%d%d",&do1[i],&do2[i]),do1[i]++,do2[i]++;
18 }
19 inline void add(int u,int v)
20 {
21     to[cnt]=v; next[cnt]=head[u]; head[u]=cnt++;
22 }
23 void create()
24 {
25     memset(head,-1,sizeof head);
26     memset(dfn,0,sizeof dfn);
27     memset(belong,0,sizeof belong);
28     memset(fg,0,sizeof fg);
29     cnt=0; divg=0; p=0; t=0;
30     for(int i=1;i<=2*n;i++) add(i,dir[i]+2*n);
31     for(int i=1;i<=mid;i++)
32         add(do1[i]+2*n,do2[i]),add(do2[i]+2*n,do1[i]);
33 }
34 bool judge()
35 {
36     for(int i=1;i<=2*n;i++)
37         if(belong[i]==belong[i+2*n]) return false;
38     return true;
39 }
40 void dfs(int u)
41 {
42     t++;
43     dfn[u]=low[u]=t;
44     stack[++p]=u; fg[u]=true;
45     for(int i=head[u];~i;i=next[i])
46     {
47         if(!dfn[to[i]])
48         {
49             dfs(to[i]);
50             low[u]=min(low[u],low[to[i]]);
51         }
52         else if(fg[to[i]]) low[u]=min(low[u],dfn[to[i]]);
53     }
54     if(dfn[u]==low[u])
55     {
56         divg++;
57         int tmp=-1;
58         while(tmp!=u)
59         {
60             tmp=stack[p--];
61             belong[tmp]=divg;
62             fg[tmp]=false;
63         }
64     }
65 }
66 bool tarjan()
67 {
68     create();
69     for(int i=1;i<=4*n;i++)
70         if(!dfn[i]) dfs(i);
71     if(judge()) return true;
72     else return false;
73 }
74 void go()
75 {
76     int l=0,r=m;
77     while(l<=r)
78     {
79         mid=(l+r)>>1;
80         //printf("%d\n",mid);
81         if(tarjan()) ans=mid,l=mid+1;
82         else r=mid-1;
83     }
84     printf("%d\n",ans);
85 }
86 int main()
87 {
88     while(scanf("%d%d",&n,&m),n||m)
89     {
90         read();
91         go();
92     }
93     system("pause");
94     return 0;
95 }
没有人能阻止我前进的步伐,除了我自己!
原文地址:https://www.cnblogs.com/proverbs/p/2667015.html