坐井观天

In the name of dream

导航

pku3207 2-SAT问题入门

Posted on 2011-07-16 00:22  一毛_  阅读(195)  评论(0编辑  收藏  举报

【题目链接】http://poj.org/problem?id=3207

【题目大意】

      给n,m(n<1000,m<500),表示 一个圆上逆时针有0,1,2,,,n-1个数,然后m对数:x,y表示x和y必须连边,边可以通过在环内来连,也可以在环外来连,问是否存在一个方案,使得这m条边都不相交,题目保证要连边的点最多只连一条边。

【分析】

     为了解决昨天训练赛的那道题,huada推荐看两篇论文,《利用对称性解决2-SAT问题》,《浅析2-SAT问题》发现看不懂,然后是看这个入门题,看了一个下午,和一个晚上,打个代码到AC又花了几个小时,我脑残,不是很想解释这几天。。。

      m条边,每条边看成一个组,组内两个成员,一个在环内,一个在环外,即这条边你可以通过在环内来连,也可以通过在环外来连,每个组必须选一个成员出来组成一个m个人的集合,这样就成了论文里的2-SAT模型了。  

      那么成员与成员之间的矛盾就是看这两个成员所代表的边是否会相交,这点根据数字的大小来判断就可以了,比如 (3,6), (5 2)显然地如果他们连在环内由于环内数是从小到大的逆时针,二者必然相交。既然相交了就矛盾根据对称性来连边了。

      连好边就一个Tarjan算法来求强连通分量,之后看这些成员是否有 同一个组的两个成员在同一个连通分量里面,如果是表示该组所代表的两个点必须在环内连一条边在环外连一条边,显然这是错误的,就可以判定答案了。 (注意在同一个连通分量里面表示二者必须同时存在或者同时不存在)。

      由于我们建边时,对m条边双层for循环,循环体里面又是四条边,所以总边是m*m*4=1000000 我脑残开到2000最后RE开到100000硬是没有开窍。。。 我极度脑残这几天状态真是糟糕透。。。。。。。。。。。。。。。。。。。。。。

pku3207代码
  1 /* 
2 * File: pku3207.cpp
3 * Author: yimao
4 * 3207 Accepted 5652K 32MS C++ 2523B 2011-07-15 23:45:44
5 * 脑残地下面邻接表存边注意关于m的双重for循环,所以边数是m*m*4=1000000,orz!
6 * Created on 2011年7月15日, 下午9:13
7 */
8
9 #include<cstdio>
10 #include<iostream>
11 #include<algorithm>
12 #include<cstring>
13 #include<cstdlib>
14 #include<cmath>
15 #include<string>
16 #include<queue>
17 #include<vector>
18 #include<map>
19 #include<set>
20 #define stop while(1);
21 #define maxint 2147483647
22 #define maxint64 9223372036854775807
23 #define maxlong 0x7FFFFFFFFFFFFFFFLL
24 using namespace std;
25 typedef unsigned long long u64;
26
27 #define maxn 1000020 //我脑残地数组开小了,边数估计错误。。。
28 int a[505],b[505];
29
30 int top;
31 struct Edge{
32 int v;
33 Edge *next;
34 }*adj[maxn],edge[maxn];
35 void Add_edge(int u,int v){
36 Edge *ptr= &edge[top++];
37 ptr->v=v;
38 ptr->next= adj[u];
39 adj[u]= ptr;
40 }
41
42 int SCCcnt; //强联通分量个数;
43 int Index; //时间戳,搜索的次序编号;
44 int Pos; //记录在栈中的位置;
45 int dfn[maxn]; //节点的搜索次序编号;
46 int low[maxn]; //节点能追溯到的最早的栈中节点的编号;
47 int belong[maxn]; //节点属于哪个强连通分量;
48 int stack[maxn],instack[maxn]; //记录节点是否在栈中;
49
50 void Tarjan(int u){
51 dfn[u]=low[u]= ++Index;
52 stack[++Pos]= u;
53 instack[u]=1;
54 for(Edge *ptr= adj[u];ptr;ptr= ptr->next){
55 int v= ptr->v;
56 if(!dfn[v]){
57 Tarjan(v);
58 low[u]= min(low[u],low[v]);
59 }
60 else if(instack[v])
61 low[u]= min(low[u],low[v]);
62 }
63 if(dfn[u]==low[u]){
64 ++SCCcnt;
65 int v;
66 do{
67 v= stack[Pos--];
68 belong[v]= SCCcnt;
69 instack[v]=0;
70 }while(u!=v);
71 }
72 }
73
74 int main(int argc, char** argv) {
75 int n,m,i,j;
76 cin>>n>>m;
77 for(i=0;i<m;i++){
78 scanf("%d%d",a+i,b+i);
79 if(a[i]>b[i]) swap(a[i],b[i]);
80 }
81 top=0;
82 memset(adj,0,sizeof(adj));
83 for(i=0;i<m;i++){
84 for(j=i+1;j<m;j++)
85 if((a[i]<a[j]&&a[j]<b[i]&&b[i]<b[j])||
86 (a[j]<a[i]&&a[i]<b[j]&&b[j]<b[i])){
87 Add_edge(2*i,2*j+1);
88 Add_edge(2*j,2*i+1);
89 Add_edge(2*j+1,2*i);
90 Add_edge(2*i+1,2*j);
91 }
92 }
93 for(i=0;i<m+m;i++) low[i]=dfn[i]=instack[i]=0;
94 SCCcnt=Index=Pos=0;
95 for(i=0;i<m+m;i++){ //这里可能不是一个连通图,所以对每个点进行一次Tarjan;
96 if(!dfn[i])
97 Tarjan(i);
98 }
99 bool Flag=0;
100 for(i=0;i<m+m;i+=2)
101 if(belong[i]==belong[i+1]){
102 Flag=1; break;
103 }
104 if(Flag) puts("the evil panda is lying again");
105 else puts("panda is telling the truth...");
106 return 0;
107 }