poj2723 Get Luffy Out 2-SAT问题

Get Luffy Out

好久没有写博客了。。

看来开学还是有一定的影响的。。

 

上次天津赛区一道2-sat问题,让我很是YM,如果和我一样,没学过,建议看看论文

 

其实2-SAT问题,就是一种逻辑判断问题,问题可以简述为:

共有N对顶点,M对不相容关系,问:能否从这N对中每对顶点中选出一顶点,共选出N个顶点,使得两两相容。

建边方式:对于两对顶点A,A'  B,B', 如果A,B不相容,则建边<A,B'>(表示选A点,就不能选B点) <B,A'>,然后就是强连通分量了

后来我发现,建边的顺序是固定的,比如上面A,B不相容,就不能建成<B', A> <A',B>,这种建边方式表示A',B'不相容,自己还琢磨了半天,真是杯具!!

 

对于这一题,有两类边:

1。有N对点 (A,B)表示钥匙A,B只能用一把,加点A',B',建边<A,B'>表示,用A钥匙,就不能用B钥匙,建边<B,A'>表示,用B钥匙,就不能用A钥匙

2。有M对点 (A,B)表示锁A,B至少得开一把,建边<A',B>,表示如果不开A锁,就必须开B锁,同样建边<B',A>

 

最后二分最大值,求强连通分量判定就行了,如果有一对顶点X,X'属于同一个强连通分支,则无解。

 

代码
   
     
1 #include < stdio.h >
2 #include < string .h >
3   #define MM 6200
4   #define NN 4200
5 typedef struct node{
6 int v;
7 struct node * nxt;
8 }NODE;
9 NODE edg[MM];
10 NODE * link[NN];
11
12   int f[NN][ 2 ];
13   int g[NN][ 2 ];
14   int N, M;
15   int idx; // 边的总数
16 int scc; // 强连通分支个数
17 int top; // 栈顶
18 int time; // 时间戳,每个节点的访问次序编号
19
20 int cnt[NN]; // 标记强连通分支i中节点数
21 int dfn[NN]; // 标记结点i的时间戳(访问次序号)
22 int low[NN]; // 记录结点u或u的子树中所有节点的最小标号
23 int stack[NN];
24 int inSta[NN]; // 标记结点是否在栈中
25 int out [NN]; // 标记分支i是否有出度
26 int root[NN]; // 标记结点i属于哪个分支
27
28 int Min( int a, int b){
29 return a < b ? a : b;
30 }
31
32 void Add( int u, int v){ // 加边
33 edg[idx].v = v;
34 edg[idx].nxt = link[u];
35 link[u] = edg + idx ++ ;
36 }
37
38 void Tarjan( int u){
39 int v;
40 dfn[u] = low[u] = ++ time;
41 stack[ ++ top] = u;
42 inSta[u] = 1 ;
43 for (NODE * p = link[u]; p; p = p -> nxt){
44 v = p -> v;
45 if (dfn[v] == 0 ){ // 未访问
46 Tarjan(v);
47 low[u] = Min(low[u], low[v]);
48 } else if (inSta[v]){
49 low[u] = Min(low[u], dfn[v]);
50 }
51 }
52 if (dfn[u] == low[u]){
53 scc ++ ;
54 do {
55 v = stack[top -- ];
56 inSta[v] = 0 ;
57 root[v] = scc;
58 cnt[scc] ++ ;
59 } while (v != u);
60 }
61 }
62
63 void Init(){
64 memset(dfn, 0 , sizeof (dfn));
65 memset(cnt, 0 , sizeof (cnt));
66 memset(inSta, 0 , sizeof (inSta));
67 time = scc = top = 0 ;
68 }
69 int Solve(){
70 int i;
71 Init();
72 for (i = 0 ; i < N * 2 ; i ++ ){
73 if ( ! dfn[i]){
74 Tarjan(i);
75 }
76 }
77 for (i = 0 ; i < N; i ++ ){
78 if (root[i] == root[i + N]) return 0 ;
79 }
80 return 1 ;
81 }
82
83 void Build( int mid){
84 int i;
85 idx = 0 ;
86 memset(link, 0 , sizeof (link));
87 for (i = 0 ; i < N / 2 ; i ++ ){
88 Add( g[i][ 0 ], g[i][1] + N);
89 Add( g[i][ 1 ], g[i][0] + N);
90 }
91 for (i = 0 ; i < mid; i ++ ){
92 Add(f[i][ 0 ] + N, f[i][ 1 ]);
93 Add(f[i][ 1 ] + N, f[i][ 0 ]);
94 }
95 }
96 int Binary(){
97 int low = 0 ;
98 int hig = M;
99 int mid, ans = 0 ;
100 while (low <= hig){
101 mid = (low + hig) >> 1 ;
102 Build(mid);
103 if (Solve()){
104 ans = mid;
105 low = mid + 1 ;
106 } else hig = mid - 1 ;
107 }
108 return ans;
109 }
110 int main()
111 {
112 int i;
113 while (scanf( " %d%d " , & N, & M) != EOF){
114 if (N == 0 && M == 0 ) break ;
115
116 for (i = 0 ; i < N; i ++ ){
117 scanf( " %d%d " , & g[i][ 0 ], & g[i][ 1 ]);
118 }
119
120 for (i = 0 ; i < M; i ++ ){
121 scanf( " %d%d " , & f[i][ 0 ], & f[i][ 1 ]);
122 }
123 N *= 2 ;
124 printf( " %d\n " , Binary());
125 }
126 return 0 ;
127 }
128

 

你可能感兴趣的:(get)