zoukankan      html  css  js  c++  java
  • POJ 3207 【2-SAT入门题 + 强连通分量】

    这道题是我对于2-SAT问题的入门题:http://poj.org/problem?id=3207

    一篇非常非常非常好的博客,很详细,认真看一遍差不多可以了解个大概:https://blog.csdn.net/JarjingX/article/details/8521690

    总结一下我对于 2-SAT 问题的初步见解:

    有很多个集合,每个集合里面有若干元素,现给出一些取元素的规则,要你判断是否可行,可行则给出一个可行方案。如果所有集合中,元素个数最多的集合有k个,那么我们就说这是一个k-sat问题,同理,2-sat问题就是k=2时的情况。

    通常我们需要将集合中点之间的边转化成点, 再将点转化成多条新边, 这些新边就要看问题是属于哪类模型。

    模型一:两者(A,B)不能同时取  

    那么选择了A就只能选择B’,选择了B就只能选择A’  

    连边A→B’,B→A’

    模型二:两者(A,B)不能同时不取
    那么选择了A’就只能选择B,选择了B’就只能选择A
    连边A’→B,B’→A

    模型三:两者(A,B)要么都取,要么都不取
    那么选择了A,就只能选择B,选择了B就只能选择A,选择了A’就只能选择B’,选择了B’就只能选择A’
    连边A→B,B→A,A’→B’,B’→A’

    模型四:两者(A,A’)必取A

    连边A’→A

    题目大意:

    一个圆环上有n个点, 点的序号从 0 到 n - 1, 给出m条边,说明点a, b之间有一条边相连接, 这条边可以在圆内部,也可以在圆的外部, 但边与边之间不能相交。求是否有满足的情况。

    解题思路:

    设对于一条边,在圆内部连接为i,圆外部连接为 i',那么两条理论上相交的边i, j(通过两条边的左右坐标来判断是否相交),就不能同时选择在圆的同一边。也就是模型一:两者(A, B)不能同时选。

    注意的是 i'只是区别于i的一条对立边, 但是这条对立边不能影响到原来所存在的边, 这里我们可以用 i + m来表示 i 的对立边。

    所以我们加边为 ,add(i,j + m), add(i + m, j), add(j, i + m), add(j + m, i)

    然后再对 2 * m 个点(包括对立点)进行tarjan, 最后判断 m 个原始边的对立边是否处在同一个强连通分量中, 若存在处在同一个强连通分量中的,则无解,否则有解。

    代码:

      1 #include<stdio.h>
      2 #include<string.h>
      3 #include<algorithm>
      4 #include<stack>
      5 #define mem(a, b) memset(a, b, sizeof(a))
      6 using namespace std;
      7 
      8 int n, m; //n个点, m条边 ,点是从 0 到 n - 1 
      9 int l[1100], r[1100];
     10 int cnt, head[1100];
     11 int dfn[1100], low[1100], vis[1100], deep, color, belong[1100];
     12 stack<int>S;
     13 
     14 void init()
     15 {
     16     mem(head, -1);
     17     cnt = 0;
     18     mem(dfn, 0);
     19     mem(low, 0);
     20     mem(vis, 0);
     21     mem(belong, 0);
     22     deep = 0;
     23     color = 0;
     24 }
     25 
     26 struct Edge
     27 {
     28     int to, next;
     29     int from;
     30 }edge[500 * 500 * 4]; //要注意这个边的数量 
     31 
     32 void add(int a, int b)
     33 {
     34     edge[++ cnt].to = b;
     35     edge[cnt].next = head[a];
     36     head[a] = cnt;
     37 }
     38 
     39 void tarjan(int now)
     40 {
     41     dfn[now] = low[now] = ++ deep;
     42     vis[now] = 1;
     43     S.push(now);
     44     for(int i = head[now]; i != -1; i = edge[i].next)
     45     {
     46         int to = edge[i].to;
     47         if(!dfn[to])
     48         {
     49             tarjan(to);
     50             low[now] = min(low[now], low[to]);
     51         }
     52         else if(vis[to])
     53             low[now] = min(low[now], dfn[to]);
     54     }
     55     if(dfn[now] == low[now])
     56     {
     57         color ++;
     58         while(1)
     59         {
     60             int temp = S.top();
     61             S.pop();
     62             vis[temp] = 0;
     63             belong[temp] = color;
     64             if(temp == now)
     65                 break;
     66         }
     67     }
     68 }
     69 
     70 int main()
     71 {
     72     int flag = 1;
     73     init();
     74     scanf("%d%d", &n, &m);
     75     for(int i = 1; i <= m; i ++)
     76     {
     77         int a, b;
     78         scanf("%d%d", &a, &b);
     79         if(a > b)
     80             swap(a, b);
     81         l[i] = a, r[i] = b;//记录每条边的左端点和右端点,保证左小右大 
     82     }
     83     for(int i = 1; i < m; i ++) //建 2-SAT 图,将边转化成点处理 
     84     {             // A B不能同时存在的模型 
     85         for(int j = i + 1; j <= m; j ++) //理论上有相交的边就转化成点来构造 2-SAT 问题的边 
     86         {
     87             if(l[i] <= l[j] && r[i] >= l[j] && r[i] <= r[j] || l[i] >= l[j] && r[i] >= r[j] && l[i] <= r[j])
     88             {
     89                 add(i, j + m);
     90                 add(i + m, j);
     91                 add(j, i + m);
     92                 add(j + m, i);
     93             }
     94         }
     95     }
     96     for(int i = 1; i <= 2 * m; i ++)//对于 i 边,其对立边为 i + m.每条边被看成点, 即总共有 2 * m个点 
     97         if(!dfn[i])
     98             tarjan(i);
     99     for(int i = 1; i <= m; i ++)//点与对立点不能同时存在一个强连通分量中, 否则问题无解 
    100     {
    101         int x = belong[i], y = belong[i + m];
    102         if(x ==  y)
    103         {
    104             flag = 0;
    105             break;
    106         }
    107     }
    108     if(flag)
    109         printf("panda is telling the truth...
    ");
    110     else
    111         printf("the evil panda is lying again
    ");
    112     return 0;
    113 }
    View Code
  • 相关阅读:
    文本切换器(TextSwitcher)的功能和用法
    图像切换器(ImageSwitcer)的功能与用法
    ViewSwitcher的功能与用法
    HTTP协议-get请求与post请求的区别
    HTTP协议缓存
    HTTP协议详解
    Vue.js----更换头像不实时更新问题
    Vue.js----date与时间戳的转换(unixTime)Moment.js让日期处理变得更简单
    Let's Encrypt 免费通配 https 签名证书 安装方法
    小程序Openid 获取,服务器 encryptedData 解密 遇到的坑
  • 原文地址:https://www.cnblogs.com/yuanweidao/p/10881886.html
Copyright © 2011-2022 走看看