zoukankan      html  css  js  c++  java
  • 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
    ");
    85         else
    86             printf("YES
    ");
    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
    ",Solve());
    110     }
    111     return 0;
    112 }
    View Code
  • 相关阅读:
    逆向测试设计
    JNLP
    3. 技术专题
    8.2. Angular使用Material控件库
    Spring Boot Actuator
    安装
    apk文件结构及反编译/代码混淆
    Visual Studio中attach进程进行调试
    .NET反编译
    3. 技术专题
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/9770598.html
Copyright © 2011-2022 走看看