一、问题描述

以你咕的模板题为例

题目描述

有\(n\)个布尔变量\(x_1\)~\(x_n\),另有\(m\)个需要满足的条件,每个条件的形式都是“\(x_i\)为true/false或\(x_j\)为true/false”。

比如“\(x_1\)为真或\(x_3\)为假”、“\(x_7\)为假或\(x_2\)为假”。

2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。

二、逻辑关系

这里不用太过专业的符号来说明逻辑关系,仅仅是感性说明(没错就是我菜)

设条件为大写字母A,B,非A,非B只条件的另一种取值

A或者B 等价于 非A则B 和 非B则A

注意到后面两个要求似乎有蜜汁对称性,而且它们都是有向的

根据后一句话,我们借助并查集扩展域的思想,把一个点的两个取值拆开,进行建图

三、建图

这里就画图举例说明吧

给出这样三个条件\((1,1)\)或\((2,1)\),\((2,1)\)或\((3,1)\),\((2,0)\)或(3,1)$

\((x,y)\)表示\(x\)需要取值\(y\)

我们把点\(x\)取值\(0\)的点编号\(x\),取值\(1\)的点编号\(x+n\)

我们可以建出如下的图

有向边\((u,v)\)表示如果\(u\)选,\(v\)一定选

很显然这是有解的,我们可以去\(1,2,3\)等点集

怎么样才会无解呢?

如果一个点的出边的点构成的集合中又出现了它自己?这样可以不选它而选另一个值啊

如果这个集合出现了另一个值呢?

好吧,这里的无解其实就是综合一下,选它的同时要选它的另一个值,选它的另一个值同时要选它

等价于两者在有向图中处于同一个环中

这样我们就可以通过tarjan求环来判断有解性了

四、构造合法方案

如果一个点没有伸出去的边,就代表它不约束别人,我们可以随便选择选或不选它

设\(cho[]\),若\(cho[x]==0\)则代表我们选择了它,否则我们没有选择它

优先选择出度为0的点

我们可以在缩点后的图中反向建图,然后做拓扑排序

注意我们选择一个点后,要把它对应的点置不选

事实上,我们也可以没必要做这么麻烦

dfs遍历的DAG图的序列反过来其实就是反向建边的拓扑序

在tarjan中先做的点的编号就是反向建边拓扑序

那么我们直接比编号大小进行赋值就可以啦

五、参考代码

#include <cstdio>
int min(int x,int y){return x<y?x:y;}
const int N=2e6+10;
int head[N],to[N<<1],Next[N<<1],cnt,n,m;
void add(int u,int v)
{
to[++cnt]=v;Next[cnt]=head[u];head[u]=cnt;
}
void init()
{
scanf("%d%d",&n,&m);
for(int i,a,j,b,k=1;k<=m;k++)
{
scanf("%d%d%d%d",&i,&a,&j,&b);
add(i+(1-a)*n,j+b*n),add(j+(1-b)*n,i+a*n);
}
}
int dfn[N],low[N],in[N],s[N],ha[N],tot,dfs_clock,n_;
void tarjan(int now)
{
dfn[now]=low[now]=++dfs_clock;
s[++tot]=now;
in[now]=1;
for(int i=head[now];i;i=Next[i])
{
int v=to[i];
if(!dfn[v])
{
tarjan(v);
low[now]=min(low[now],low[v]);
}
else if(in[v])
low[now]=min(low[now],dfn[v]);
}
if(low[now]==dfn[now])
{
int k;++n_;
do
{
k=s[tot--];
ha[k]=n_;
in[k]=0;
}while(k!=now);
}
}
void work()
{
for(int i=1;i<=n<<1;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++)
if(ha[i]==ha[i+n])
{
printf("IMPOSSIBLE\n");
return;
}
printf("POSSIBLE\n");
for(int i=1;i<=n;i++)
printf("%d ",ha[i]>ha[i+n]);
}
int main()
{
init();
work();
return 0;
}

2018.9.1

最新文章

  1. Bootstrap&lt;基础十三&gt; 按钮组
  2. Redis哈希表的实现要点
  3. C++11:POD数据类型
  4. Kafka入门学习随记(二)
  5. Codevs 1063 合并果子
  6. CSS3 animation的steps方式过渡
  7. EditText操作收集
  8. 关于git的一些常用命令
  9. 织梦CMS去广告方法 for DedeCMS V5.7
  10. 查找算法(I) 顺序查找 二分查找 索引查找
  11. nginx location的命中过程
  12. redis 系列26 Cluster高可用 (1)
  13. shell入门(二):()、(())、[]、[[]]、{}
  14. CodeMirror 使用
  15. 线程--实现Runnable接口
  16. jquery禁用a标签
  17. jdk8中关于操作集合的一些新特性,遍历和排序操作
  18. Linux运行Java出现“Exception in thread &quot;main&quot; java.lang.OutOfMemoryError: Java heap space”报错
  19. 性能测试之mysql监控、优化
  20. [Swift实际操作]七、常见概念-(11)路径URL的使用详解

热门文章

  1. 【学时总结】◆学时&#183;V◆ 逆元法
  2. Selenium页面加载策略
  3. DNS介绍与安装使用
  4. Nagios 监控Windows服务器(详细篇)
  5. linux-shell——01
  6. POJ:1995-Raising Modulo Numbers(快速幂)
  7. 常用 Git 命令清单【转--阮一峰】
  8. 关于学习less后一些感悟
  9. div嵌套img高度不相同
  10. MyBatis---简单关系查询