• 【模板】2-SAT 问题


    嘟嘟嘟

     

    SAT以及2-SAT问题是啥以及定义我就不叨叨了,直接讲怎么求。

    比如有一个条件:a || b,就是说a,b必须至少有一个为真。这个可以转化为(!a => b && !b => a),那么就可以连边建图了:首先把每一个点x拆成x和!x,那么对于上述条件,连两条边(!a -> b)和(!b -> a)。类似的,对于a和b的其他条件,我们也可以模仿着连边。

    接下来怎么判断是否有可行解呢?可以用tarjan强连通分量缩点,对于属于一个强连通分量的点,他们的取值一定都是相同的(就是都是真或者都是假)。那么如果x和!x在一个强连通分量中,则必定无解,因为x为真和!x为真之可能满足一个。

    那么接下来应该输出合法的一组解。一般2-SAT问题都有spj,所以任意一组就行。

     要分两种情况:

    1.如果x的拓扑序在!x前面,那么x一定是0.因为x一定有一条路径通向!x,如果x是1,一定能推出!x是1,也就是x是0,矛盾了。

    2.如果x拓扑序在!x后面,那么x必定是1。仿照上述思想,如果!x为真,说明x = 0,能推出x为真,即x = 1,矛盾。如果x = 1,则!x为假,不一定能推出x。所以x必须是1。

    不过tarjan缩完点后的拓扑序是反的,所以col[x] < col[x + n]说明x在!x后面;反之同理。

    讲完了。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 #include<cmath>
     5 #include<cstring>
     6 #include<cstdlib>
     7 #include<cctype>
     8 #include<stack>
     9 #include<queue>
    10 #include<vector>
    11 using namespace std;
    12 #define enter puts("")
    13 #define space putchar(' ')
    14 #define Mem(a, x) memset(a, x, sizeof(a))
    15 #define rg register
    16 typedef long long ll;
    17 typedef double db;
    18 const int INF = 0x3f3f3f3f;
    19 const db eps = 1e-8;
    20 const int maxn = 1e6 + 5;
    21 inline ll read()
    22 {
    23   ll ans = 0;
    24   char ch = getchar(), las = ' ';
    25   while(!isdigit(ch)) las = ch, ch = getchar();
    26   while(isdigit(ch)) ans = ans * 10 + ch - '0', ch = getchar();
    27   if(las == '-') ans = -ans;
    28   return ans;
    29 }
    30 inline void write(ll x)
    31 {
    32   if(x < 0) putchar('-'), x = -x;
    33   if(x >= 10) write(x / 10);
    34   putchar(x % 10 + '0');
    35 }
    36 
    37 int n, m;
    38 struct Edge
    39 {
    40   int to, nxt;
    41 }e[maxn << 1];
    42 int head[maxn << 1], ecnt = 0;
    43 void addEdge(int x, int y)
    44 {
    45   e[++ecnt].to = y;
    46   e[ecnt].nxt = head[x];
    47   head[x] = ecnt;
    48 }
    49 
    50 stack<int> st;
    51 bool in[maxn << 1];
    52 int dfn[maxn << 1], low[maxn << 1], cnt = 0;
    53 int col[maxn << 1], ccol = 0;
    54 void tarjan(int now)
    55 {
    56   dfn[now] = low[now] = ++cnt;
    57   st.push(now); in[now] = 1;
    58   for(int i = head[now]; i; i = e[i].nxt)
    59     {
    60       if(!dfn[e[i].to])
    61     {
    62       tarjan(e[i].to);
    63       low[now] = min(low[now], low[e[i].to]);
    64     }
    65       else if(in[e[i].to]) low[now] = min(low[now], dfn[e[i].to]);
    66     }
    67   if(dfn[now] == low[now])
    68     {
    69       int x; ++ccol;
    70       do
    71     {
    72       x = st.top(); st.pop();
    73       col[x] = ccol; in[x] = 0;
    74     }while(x != now);
    75     }
    76 }
    77 
    78 int main()
    79 {
    80   n = read(); m = read();
    81   for(int i = 1; i <= m; ++i)
    82     {
    83       int x = read(), flg1 = read(), y = read(), flg2 = read();
    84       addEdge(x + flg1 * n, y + (flg2 ^ 1) * n);
    85       addEdge(y + flg2 * n, x + (flg1 ^ 1) * n);
    86     }
    87   for(int i = 1; i <= (n << 1); ++i) if(!dfn[i]) tarjan(i);
    88   for(int i = 1; i <= n; ++i) if(col[i] == col[n + i])
    89       printf("IMPOSSIBLE
    "); return 0;
    90     }
    91   printf("POSSIBLE
    ");
    92   for(int i = 1; i <= n; ++i) write(col[i] < col[i + n]), space;
    93   enter;
    94   return 0;
    95 }
    View Code
  • 相关阅读:
    记一次性能优化经历
    把一个一中的字段更新另一个表中的t-sql
    Dapper 中使用sql in 关键字查询
    HTML5 学习笔记 应用程序缓存
    HTML5学习笔记 Web存储
    HTML5 学习笔记 表单属性
    HTML5学习笔记 Geolocation(地理定位)
    vim插件之delimitMate.vim
    vim 插件之 surround.vim
    vim 脚本之快速打印log
  • 原文地址:https://www.cnblogs.com/mrclr/p/9759556.html
Copyright © 2020-2023  润新知