Pentium.Labs

System全家桶:https://zhuanlan.zhihu.com/c_1238468913098731520

导航

poj3207 2-SAT入门

一开始题意没读懂 = =

 

题意:比如说对于表盘上a到b、c到d都要连边,这两个边不能交叉。这两个边要么都在圆内要么都在圆外,而且可以是曲线= =

比如这种情况:(Reference:http://blog.csdn.net/l04205613/article/details/6668318)

(左边情况看似是合法的,但是一个边在圆内一个在圆外,形成矛盾了)

 

这回只需判断是否可行不用输出方案,这样就简单多了

STEP:构图,矛盾的地方连边->tarjan求scc->判断是否a和not a在同一分量内

 

  1 #include "iostream"
  2 #include "cstring"
  3 #include "stack"
  4 using namespace std;
  5 #define maxn 2010
  6 
  7 int G[maxn][maxn],dx[maxn],dy[maxn];
  8 int pre[maxn],lowlink[maxn],sccno[maxn];
  9 int dfs_clock,scc_cnt,N,M;
 10 stack<int> St;
 11 
 12 void tarjan(int u)
 13 {
 14     pre[u]=lowlink[u]=++dfs_clock;
 15     St.push(u);
 16     for (int i=1;i<=G[u][0];i++)
 17     {
 18         int v=G[u][i];
 19         if (!pre[v])
 20         {
 21             tarjan(v);
 22             lowlink[u]=min(lowlink[u],lowlink[v]);
 23         }
 24         else if (!sccno[v])
 25         {
 26             lowlink[u]=min(lowlink[u],pre[v]);
 27         }
 28     }
 29     if (lowlink[u]==pre[u])
 30     {
 31         scc_cnt++;
 32         for (;;)
 33         {
 34             int x=St.top();
 35             St.pop();
 36             sccno[x]=scc_cnt;
 37             if (x==u) break;
 38         }
 39     }
 40 }
 41 
 42 void find_scc(int n)
 43 {
 44     dfs_clock=scc_cnt=0;
 45     memset(sccno,0,sizeof(sccno));
 46     memset(pre,0,sizeof(pre));
 47     for (int i=1;i<=n;i++)
 48         if (!pre[i])
 49             tarjan(i);
 50 }
 51 
 52 void add_edge(int x,int y)
 53 {
 54     G[x][0]++;
 55     G[x][G[x][0]]=y;
 56 }
 57 
 58 int main()
 59 {
 60     cin>>N>>M;
 61     for (int i=1;i<=M;i++)
 62         cin>>dx[i]>>dy[i];
 63 
 64     //1->N:dx[i]->dy[i]走圆内
 65     //N+1->2*N:dx[i]->dy[i]走圆外
 66     for (int i=1;i<=N;i++)
 67     {
 68         for (int j=i+1;j<=N;j++)
 69         {
 70             if ((dx[j]<dx[i])&&(dx[i]<dy[j])&&(dy[j]<dy[i]))
 71             {
 72                 add_edge(i,j+N);
 73                 add_edge(j+N,i);
 74                 add_edge(j,i+N);
 75                 add_edge(i+N,j);
 76             }
 77             if ((dx[i]<dx[j])&&(dx[j]<dy[i])&&(dy[i]<dy[j]))
 78             {
 79                 add_edge(i,j+N);
 80                 add_edge(j+N,i);
 81                 add_edge(j,i+N);
 82                 add_edge(i+N,j);
 83             }
 84         }
 85     }
 86 
 87     find_scc(2*N);
 88 
 89     for (int i=1;i<=N;i++)
 90     {
 91         if (sccno[i]==sccno[i+N])
 92         {
 93             cout<<"the evil panda is lying again"<<endl;
 94             return 0;
 95         }
 96     }
 97     cout<<"panda is telling the truth..."<<endl;
 98     return 0;
 99 
100 }
View Code

 

------------------------------------------------------------------------------------------------

注意:对于2-SAT问题构图的时候有些题还要纠结一下是连单向边(如poj3678)还是双向边(如本题),

reference:http://blog.csdn.net/u011026968/article/details/10823853

 

本题中,我们以第二个判断条件if ((dx[i]<dx[j])&&(dx[j]<dy[i])&&(dy[i]<dy[j]))为例:

若j在圆内,那么i一定在圆外

若j在圆外,那么i一定在圆内

若i在圆内,那么j一定在圆外

若i在圆外,那么j一定在圆内

注意蓝色部分就是双向边了

------------------------------------

而poj 3683题中:

我们以构图时的第一个判断条件if (min(S[i]+D[i],S[j]+D[j])>max(S[i],S[j]))为例,

若i在前段举行,那么j一定在后半段举行

若j在后半段举行,那么i不一定要在前半段举行

若j在前段举行,那么i一定在后半段举行

若i在后半段举行,那么j不一定要在前半段举行

注意红色部分:因为不确定(本判断条件中并没有讨论后半段的时间),所以不能双向边

 

posted on 2014-10-29 22:02  Pentium.Labs  阅读(238)  评论(0编辑  收藏  举报



Pentium.Lab Since 1998