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

 

posted on 2010-09-14 21:33  ylfdrib  阅读(960)  评论(0编辑  收藏  举报