hdu 3062(2-SAT入门题)

传送门

 

题解:

  根据矛盾关系构造布尔公式。

  令 0~n-1 表示妻子, n~2*n-1 表示丈夫

  A1 C1

  A2 C2

  如果C1为0,C2为0,对应的布尔公式为 (A1+n)V(A2+n)

  如果C1为0,C2为1,对应的布尔公式为 (A1+n)V(A2)

  如果C1为1,C2为0,对应的布尔公式为 (A1)V(A2+n)

  如果C1为1,C2为1,对应的布尔公式为 (A1)V(A2)

  判断是否存在一组解,使得所有满足条件的合取范式 ((A1+n)V(A2+n))∧((A1+n)V(A2))∧((A1)V(A2+n))∧((A1)V(A2)) 的值为真;

  如果存在,输出YES,反之,输出NO.

  讲析取式转化成两个蕴含式,以此构图,然后进行强连通分量分解,求出所属强连通分量的拓扑序。

  如果有 scc[i] == scc[n+i] 则无解,输出NO,反之,一定有解,输出YES。

AC代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<vector>
 4 #include<cstring>
 5 using namespace std;
 6 #define pb push_back
 7 #define mem(a,b) (memset(a,b,sizeof a))
 8 const int maxn=1e3+50;
 9 
10 int n,m;
11 int scc[2*maxn];
12 bool vis[2*maxn];
13 vector<int >vs;
14 vector<int >G[2*maxn],rG[2*maxn];
15 void addEdge(int u,int v)
16 {
17     G[u].pb(v);
18     rG[v].pb(u);
19 }
20 void Dfs(int u)
21 {
22     vis[u]=true;
23     for(int i=0;i < G[u].size();++i)
24     {
25         int to=G[u][i];
26         if(!vis[to])
27             Dfs(to);
28     }
29     vs.pb(u);
30 }
31 void rDfs(int u,int k)
32 {
33     scc[u]=k;
34     vis[u]=true;
35     for(int i=0;i < rG[u].size();++i)
36     {
37         int to=rG[u][i];
38         if(!vis[to])
39             rDfs(to,k);
40     }
41 }
42 void SCC()
43 {
44     mem(vis,false);
45     vs.clear();
46     for(int i=0;i < 2*n;++i)
47         if(!vis[i])
48             Dfs(i);
49     int k=0;
50     mem(vis,false);
51     for(int i=vs.size()-1;i >= 0;--i)
52     {
53         int u=vs[i];
54         if(!vis[u])
55             rDfs(u,++k);
56     }
57 }
58 void Init()
59 {
60     for(int i=0;i < 2*maxn;++i)
61         G[i].clear(),rG[i].clear();
62 }
63 int main()
64 {
65     while(~scanf("%d%d",&n,&m))
66     {
67         Init();
68         for(int i=1;i <= m;++i)
69         {
70             int a1,c1;
71             int a2,c2;
72             scanf("%d%d%d%d",&a1,&a2,&c1,&c2);
73             int x=a1+(c1 == 0 ? 1:0)*n;//矛盾关系对应的布尔公式为(xVy),转化成两个蕴含式即为有向图中对应的边
74             int y=a2+(c2 == 0 ? 1:0)*n;
75             addEdge((x >= n ? x-n:x+n),y);
76             addEdge((y >= n ? y-n:y+n),x);
77         }
78         SCC();
79         bool flag=false;
80         for(int i=0;i < n;++i)
81             if(scc[i] == scc[i+n])
82                 flag=true;
83         if(flag)
84             printf("NO\n");
85         else
86             printf("YES\n");
87     }
88 }
View Code

 


 

分割线

从第一次学习2-sat,第一次AC这道题到今天,已经过去半年时间了;

这半年时间,成长了不少;

这两天,重新拾起2-sat的知识,理解上要透彻了些许,代码风格也变化了不少;

下面说说现在的我会对此题做如何解析:

  假设ui,vi 分别代表第 i 对夫妇的妻子和丈夫,uj,vj 同理;

  如果 ui 与 uj 矛盾,那么,构造的边就是:

  ui -> vj , vj -> ui;

  uj -> vi , vi -> uj;

  跑一边强连通分量分解,判断ui和vi是否在同一个强连通分量中即可;

AC代码:

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #include<vector>
  5 using namespace std;
  6 #define pb(x) push_back(x)
  7 #define mem(a,b) memset(a,b,sizeof(a))
  8 const int maxn=(1e3+50);
  9 
 10 int n,m;
 11 int couple[maxn][2];
 12 int num;
 13 int head[maxn<<1];
 14 struct Edge
 15 {
 16     int to;
 17     int next;
 18 }G[maxn*maxn<<1];
 19 void addEdge(int u,int v)
 20 {
 21     G[num]={v,head[u]};
 22     head[u]=num++;
 23 }
 24 struct SCC
 25 {
 26     int col[maxn<<1];
 27     bool vis[maxn<<1];
 28     vector<int >vs;
 29     void DFS(int u)
 30     {
 31         vis[u]=true;
 32         for(int i=head[u];~i;i=G[i].next)
 33         {
 34             int v=G[i].to;
 35             if((i&1) || vis[v])//正向边,num为偶数
 36                 continue;
 37             DFS(v);
 38         }
 39         vs.push_back(u);//拓扑序
 40     }
 41     void RDFS(int u,int k)//反向求解强连通分量
 42     {
 43         vis[u]=true;
 44         col[u]=k;
 45         for(int i=head[u];~i;i=G[i].next)
 46         {
 47             int v=G[i].to;
 48             if(!(i&1) || vis[v])//反向边,num为奇数
 49                 continue;
 50             RDFS(v,k);
 51         }
 52     }
 53     int scc()
 54     {
 55         vs.clear();
 56         mem(vis,false);
 57         for(int i=0;i < (n<<1);++i)
 58             if(!vis[i])
 59                 DFS(i);
 60 
 61         int k=0;
 62         mem(vis,false);
 63         for(int i=vs.size()-1;i >= 0;--i)//从拓扑序的最大值开始查找SCC
 64             if(!vis[vs[i]])
 65                 RDFS(vs[i],++k);
 66         return k;//强连通分量的个数
 67     }
 68 }_scc;
 69 char *Solve()
 70 {
 71     _scc.scc();
 72     for(int i=0;i < n;++i)
 73         if(_scc.col[couple[i][0]] == _scc.col[couple[i][1]])
 74             return "NO";
 75     return "YES";
 76 }
 77 void Init()
 78 {
 79     num=0;
 80     mem(head,-1);
 81 }
 82 int main()
 83 {
 84 //    freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin);
 85     while(~scanf("%d%d",&n,&m))
 86     {
 87         Init();//不要忘啊
 88         int cnt=0;
 89         for(int i=0;i < n;++i)
 90         {
 91             couple[i][0]=cnt++;///妻子编号
 92             couple[i][1]=cnt++;///丈夫编号
 93         }
 94         for(int i=1;i <= m;++i)
 95         {
 96             int a1,a2;
 97             int c1,c2;
 98             scanf("%d%d%d%d",&a1,&a2,&c1,&c2);
 99             int u1=couple[a1][c1];
100             int v1=couple[a1][!c1];
101             int u2=couple[a2][c2];
102             int v2=couple[a2][!c2];
103             ///u1,u2不相容
104             addEdge(u1,v2);
105             addEdge(v2,u1);///反向边
106             addEdge(u2,v1);
107             addEdge(v1,u2);///反向边
108         }
109         printf("%s\n",Solve());
110     }
111     return 0;
112 }
View Code

 

posted @ 2018-10-11 09:20  HHHyacinth  阅读(337)  评论(1编辑  收藏  举报