2-SAT问题

这是一道2-SAT的模板题。对于2-SAT问题的每一个条件,我们需要把他们转化成可接受的条件。即"若变量A的赋值为x,则变量B的赋值为y",其中x,y均等于0或1。

对于每个条件我们连一条有向边,下面对于本题给出的或举例。

假如题目的限制条件为 X=1 or Y = 1 为真

那么对于这个条件,我们可以按上述方法,转换成2-SAT可接受的判定条件:

如果X=0,那么Y=1 以及 如果Y=0,那么X=1

如果题目一共给出n个变量,我们通常假定[1..n]代表变量X[i]赋值为0,[n+1..2n]代表变量x[i]赋值为1。

对于上面的例子,即在(x,y+n),(y,x+n)连有向边即可。

在判定是否矛盾的时候,只要用tarjan找出所有强联通分量,如果x与x+n在同一强联通分量中,说明问题无解。否则有解。

那么如何构造一组解呢?

首先,在一个SCC中,只要确定了一个变量的值,那么该SCC中其他变量的值也确定了。我们再考虑一个点,假设这个点在原图中没有出度,那么这个点的取值将不会影响其他店。

那么我们得到启发,把所有强联通分量缩点,依次取出度为0的点的赋值情况即可。

找出度为0的点,我们可以用tarjan缩点后重构图,但是注意要建反图,再利用topsort找入度为0的点的性质topsort一次。

缩点之后我们可以发现scc[x],scc[x+n]是原图对称的两个点,假如有两个点在原图中属于同一个scc,那么他们的对称点也一定属于同一个scc(感性理解。。大概是逆否命题的感觉?)

那么我们记opp[scc[x]]=scc[x+n], opp[scc[x+n]]=scc[x];

当我们把队首元素取出来的时候,用val记录该ssc中点的赋值情况,意思为该联通分量中所有原图的点(小于n的点)的取值合法,初始值为-1。如果val=-1,那么val[s]=0, val[opp[s]]=1。

这样赋值的意义是,如果该scc中有某变量取值是0,那么代表他的取值的点一定小于n,令改点为s,最后我们在构造解的时候直接取val[scc[s]]=0即可。如果该scc中有某变量取值是1,那么他的对称点所在的scc被赋值成了1,也就是说序号小于n的那个点所在的scc的val为1,构造解的时候同理,直接取值即可。

下面是这种构造方法的代码:

  1. #include <bits/stdc++.h>
  2. #define INF 0x3f3f3f3f
  3. #define full(a, b) memset(a, b, sizeof a)
  4. using namespace std;
  5. typedef long long ll;
  6. inline int lowbit(int x){ return x & (-x); }
  7. inline int read(){
  8. int X = 0, w = 0; char ch = 0;
  9. while(!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
  10. while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
  11. return w ? -X : X;
  12. }
  13. inline int gcd(int a, int b){ return a % b ? gcd(b, a % b) : b; }
  14. inline int lcm(int a, int b){ return a / gcd(a, b) * b; }
  15. template<typename T>
  16. inline T max(T x, T y, T z){ return max(max(x, y), z); }
  17. template<typename T>
  18. inline T min(T x, T y, T z){ return min(min(x, y), z); }
  19. template<typename A, typename B, typename C>
  20. inline A fpow(A x, B p, C lyd){
  21. A ans = 1;
  22. for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
  23. return ans;
  24. }
  25. const int N = 2000005;
  26. int n, m, cnt, k, head[N], dfn[N], low[N], ins[N], tot, scc[N];
  27. int h[N], t, deg[N], val[N], opp[N];
  28. struct Edge { int v, next; } edge[N<<2], e[N<<2];
  29. stack<int> st;
  30. void addEdge(int a, int b){
  31. edge[cnt].v = b, edge[cnt].next = head[a], head[a] = cnt ++;
  32. }
  33. void add(int a, int b){
  34. e[t].v = b, e[t].next = h[a], h[a] = t ++;
  35. }
  36. void tarjan(int s){
  37. dfn[s] = low[s] = ++k;
  38. ins[s] = true, st.push(s);
  39. for(int i = head[s]; i != -1; i = edge[i].next){
  40. int u = edge[i].v;
  41. if(!dfn[u]){
  42. tarjan(u);
  43. low[s] = min(low[s], low[u]);
  44. }
  45. else if(ins[u])
  46. low[s] = min(low[s], dfn[u]);
  47. }
  48. if(dfn[s] == low[s]){
  49. tot ++; int cur = 0;
  50. do{
  51. cur = st.top(); st.pop();
  52. ins[cur] = false, scc[cur] = tot;
  53. }while(cur != s);
  54. }
  55. }
  56. void topSort(){
  57. full(val, -1), full(h, -1);
  58. for(int s = 1; s <= 2 * n; s ++){
  59. for(int i = head[s]; i != -1; i = edge[i].next){
  60. int u = edge[i].v;
  61. if(scc[s] != scc[u]) add(scc[u], scc[s]), deg[scc[s]] ++;
  62. }
  63. }
  64. queue<int> q;
  65. for(int i = 1; i <= tot; i ++){
  66. if(!deg[i]) q.push(i);
  67. }
  68. while(!q.empty()){
  69. int s = q.front(); q.pop();
  70. if(val[s] == -1) val[s] = 0, val[opp[s]] = 1;
  71. for(int i = h[s]; i != -1; i = e[i].next){
  72. int u = e[i].v;
  73. if(!--deg[u]) q.push(u);
  74. }
  75. }
  76. for(int i = 1; i <= n; i ++){
  77. if(val[scc[i]]) printf("1 ");
  78. else printf("0 ");
  79. }
  80. puts("");
  81. }
  82. int main(){
  83. full(head, -1);
  84. n = read(), m = read();
  85. for(int i = 0; i < m; i ++){
  86. int x = read(), a = read(), y = read(), b = read();
  87. addEdge(x + (1 - a) * n, y + b * n);
  88. addEdge(y + (1 - b) * n, x + a * n);
  89. }
  90. for(int i = 1; i <= 2 * n; i ++){
  91. if(!dfn[i]) tarjan(i);
  92. }
  93. bool flag = false;
  94. for(int i = 1; i <= n; i ++){
  95. if(scc[i] == scc[i + n]){
  96. printf("IMPOSSIBLE\n");
  97. flag = true;
  98. break;
  99. }
  100. }
  101. if(!flag){
  102. printf("POSSIBLE\n");
  103. for(int i = 1; i <= n; i ++){
  104. opp[scc[i]] = scc[i + n], opp[scc[i + n]] = scc[i];
  105. }
  106. topSort();
  107. }
  108. return 0;
  109. }

但是,其实我们考虑tarjan的本质,是一次dfs,联通分量序号越小的在图的越底层。

也就是说我们用tarjan找到的scc其实已经满足我们需要的自底向上的性质了。

我们对每个点的取值情况,直接取靠后的ssc即可。

下面给出代码,无需重构图:

  1. #include <bits/stdc++.h>
  2. #define INF 0x3f3f3f3f
  3. #define full(a, b) memset(a, b, sizeof a)
  4. using namespace std;
  5. typedef long long ll;
  6. inline int lowbit(int x){ return x & (-x); }
  7. inline int read(){
  8. int X = 0, w = 0; char ch = 0;
  9. while(!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
  10. while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
  11. return w ? -X : X;
  12. }
  13. inline int gcd(int a, int b){ return a % b ? gcd(b, a % b) : b; }
  14. inline int lcm(int a, int b){ return a / gcd(a, b) * b; }
  15. template<typename T>
  16. inline T max(T x, T y, T z){ return max(max(x, y), z); }
  17. template<typename T>
  18. inline T min(T x, T y, T z){ return min(min(x, y), z); }
  19. template<typename A, typename B, typename C>
  20. inline A fpow(A x, B p, C lyd){
  21. A ans = 1;
  22. for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
  23. return ans;
  24. }
  25. const int N = 2000005;
  26. int n, m, cnt, k, head[N], dfn[N], low[N], ins[N], tot, scc[N];
  27. struct Edge { int v, next; } edge[N<<2];
  28. stack<int> st;
  29. void addEdge(int a, int b){
  30. edge[cnt].v = b, edge[cnt].next = head[a], head[a] = cnt ++;
  31. }
  32. void tarjan(int s){
  33. dfn[s] = low[s] = ++k;
  34. ins[s] = true, st.push(s);
  35. for(int i = head[s]; i != -1; i = edge[i].next){
  36. int u = edge[i].v;
  37. if(!dfn[u]){
  38. tarjan(u);
  39. low[s] = min(low[s], low[u]);
  40. }
  41. else if(ins[u])
  42. low[s] = min(low[s], dfn[u]);
  43. }
  44. if(dfn[s] == low[s]){
  45. tot ++; int cur = 0;
  46. do{
  47. cur = st.top(); st.pop();
  48. ins[cur] = false, scc[cur] = tot;
  49. }while(cur != s);
  50. }
  51. }
  52. int main(){
  53. full(head, -1);
  54. n = read(), m = read();
  55. for(int i = 0; i < m; i ++){
  56. int x = read(), a = read(), y = read(), b = read();
  57. addEdge(x + (1 - a) * n, y + b * n);
  58. addEdge(y + (1 - b) * n, x + a * n);
  59. }
  60. for(int i = 1; i <= 2 * n; i ++){
  61. if(!dfn[i]) tarjan(i);
  62. }
  63. bool flag = false;
  64. for(int i = 1; i <= n; i ++){
  65. if(scc[i] == scc[i + n]){
  66. printf("IMPOSSIBLE\n");
  67. flag = true;
  68. break;
  69. }
  70. }
  71. if(!flag){
  72. printf("POSSIBLE\n");
  73. for(int i = 1; i <= n; i ++)
  74. printf("%d ", scc[i] > scc[i + n]);
  75. }
  76. return 0;
  77. }

洛谷P4782 2-SAT问题的更多相关文章

  1. 洛谷P4782 【模板】2-SAT问题 [2-SAT]

    题目传送门 [模板]2-SAT问题 题目背景 2-SAT 问题 模板 题目描述 有n个布尔变量 $x_1/~x_n$​ ,另有$m$个需要满足的条件,每个条件的形式都是“ $x_i$ 为$true/f ...

  2. 洛谷P-4782 2-sat+Tarjan

    https://www.luogu.org/problemnew/solution/P4782 这里的大佬已经说的够好了 #include<iostream> #include<cs ...

  3. 【刷题】洛谷 P4782 【模板】2-SAT 问题

    题目背景 2-SAT 问题 模板 题目描述 有n个布尔变量 \(x_1\)​~\(x_n\)​,另有m个需要满足的条件,每个条件的形式都是"\(x_i\)​为true/false或\(x_j ...

  4. [洛谷P4782]【模板】2-SAT 问题

    题目大意:有$n$个布尔变量 $x_1 \sim x_n$,另有$m$个需要满足的条件,每个条件的形式都是"$x_i$ 为$true/false$或$x_j$为$true/false$&qu ...

  5. [洛谷P4782] [模板] 2-SAT 问题

    NOIp后第一篇题解. NOIp我考的很凉啊...... 题目传送门 之前讲过怎么判断2-SAT是否存在解. 至于如何构造一组解: 我们想到对tarjan缩点后的图进行拓扑排序. 那么对于代表0状态的 ...

  6. 2-SAT问题学习笔记+例题[洛谷P4792]

    一个不错的2-SAT文章:传送门 问题初入 什么是2-SAT SAT是适定性(Satisfiability)问题的简称 .一般形式为k-适定性问题,简称 k-SAT. 首先,把「2」和「SAT」拆开. ...

  7. 洛谷 P3695 CYaRon!语 题解 【模拟】【字符串】

    大模拟好啊! 万一远古计算机让我写个解释器还真是得爆零了呢. 题目背景 「千歌です」(我是千歌).「曜です」(我是曜).「ルビィです」(我是露比).「3人合わせて.We are CYaRon! よろし ...

  8. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

  9. 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.

    没有上司的舞会  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond       题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...

随机推荐

  1. xadmin后台页面的自定制

    01-自定制页面 注:最近找到了更好的解决办法:重写钩子函数版  https://www.cnblogs.com/pgxpython/p/10593507.html 需求背景:根据要实现的功能需求,x ...

  2. centos7下zabbix安装与部署

    1.Zabbix介绍 zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制以让系 ...

  3. sql中return和returns的区别

    用户定义函数中,用RETURNS 子句指定该函数返回值的数据类型 return用于返回具体的值/值变量

  4. charles如何设置弱网

  5. mysql_单表查询

    单表查询 比较符 当在子句中使用 <. >. <=. >=. <>或 !=时,字段的名字不加引号.如果使用引号,则查询出的结果为空,并且不会报错. ; 等号 当在子 ...

  6. GFS浅析

    1 . 简介 GFS, Big Table, Map Reduce称为Google的三驾马车,是许多基础服务的基石 GFS于2003年提出,是一个分布式的文件系统,与此前的很多分布式系统的前提假设存在 ...

  7. 分析一个react项目

    目录结构 下面是使用npx create-react-app web-app来创建的一个项目(已经删除了多余的文件) web-app ├── node_modules │   ├── ....... ...

  8. nginx配置ssl证书后无法访问https

    一直听说https更安全,要安装证书,一直没试过,今天终于试了试 首先得有个http的域名网站,服务器. 到阿里云的安全-ssl证书管理申请一个免费的,可以绑定一个域名  然后完善资料,照着例子配置一 ...

  9. LLVM的安装

    1. 官网下载 llvm 2. 官网下载cmake 3. configure 执行 llvm 发现报错 4. 解压缩 cmake 5.将cmake 下面的bin 目录放到环境变量里面去 6. 创建一个 ...

  10. Linux基础学习笔记1

    MBR分区 主分区: 1-4,一块硬盘最多四个主分区,对主机必须有,主区可以格式化ntfs,存数据: 扩展分区:1-4,一块硬盘最多一个扩展分区,可以没有扩展分区,划分更小的单元,即逻辑分区: 逻辑分 ...