前言

\(2-SAT\)的解法不止一种(例如暴搜?),但最高效的应该还是\(Tarjan\)

说来其实我早就写过用\(Tarjan\)求解\(2-SAT\)的题目了(就是这道题:【2019.8.14 慈溪模拟赛 T2】黑心老板(gamble)),这篇博客本来已经石沉大海,打算坑掉了的,但由于在\(CSP-S\)前复习板子时忘记了这道题写法,结果板子题都挂了好几次,为了加深印象,为了自我惩罚,来补博客了。

基本模型

什么是\(2-SAT\)?

考虑有\(n\)个物品,每个物品有\(0\)和\(1\)两种取值。给出一些诸如第\(i\)个物品是\(0/1\)或第\(j\)个物品是\(0/1\)的限制,如:

  • 第\(1\)个物品是\(0\)或第\(2\)个物品是\(1\)。
  • 第\(1\)个物品是\(1\)或第\(3\)个物品是\(1\)。
  • ......

而\(2-SAT\),就是求出一个满足所有条件的可行解,或是判断无解。

建图

既然提到要用\(Tarjan\)了,那么首先我们需要把这个问题转移到图上。

我们把每个物品看作两个点,分别表示这个物品取\(0\)和取\(1\)。

由于题目中给出的限制不是很明确,所以我们要先将它进行转化。

例如,若题目中给出第\(i\)个物品是\(x\)或第\(j\)个物品是\(y\)。

那么如果第\(i\)个物品是\(!x\),第\(j\)个物品就必须是\(y\)。反之,如果第\(j\)个物品是\(!y\),第\(i\)个物品就必须是\(x\)。

即,将\((i,!x)\)向\((j,y)\)连边,\((j,!y)\) 向\((i,x)\)连边。

求解

首先,我们考虑,如何判断已知的一组解\(ans\)的合法性。

不难发现,若能从\((i,ans_i)\)走到\((i,!ans_i)\),就说明这组解是不合法的。

这无疑带给我们一些启发。

如果存在\((i,0)\)与\((i,1)\)能互相到达,那么就是无解的。否则一定有解。

结合强连通分量的概念,即若\((i,0)\)与\((i,1)\)在同一个强连通分量中,就无解。

然后我们要考虑如何找到一组可行解。

如果\((i,0)\)所在的连通块能到达\((i,1)\),\(i\)就一定要选\(1\),反之亦然。

而能到达,一个必要条件就是缩点后拓扑序较小。

而要比较拓扑序,实际上也可以直接比较所在连通块缩点后的编号,根据\(Tarjan\)的原理,显然编号越小拓扑序越大。

因此我们只要选择编号较小的方案即可。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,m,ee,lnk[2*N+5];struct edge {int to,nxt;}e[2*N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C==E&&(clear(),0),*C++=c)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI,C=FO,E=FO+FS;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Tp I void write(Con Ty& x,Con char& y) {write(x),pc(y);}
I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
namespace Tarjan//Tarjan缩点
{
int d,T,cnt,dfn[2*N+5],low[2*N+5],vis[2*N+5],S[2*N+5],col[2*N+5];
I void dfs(CI x,CI lst)//Tarjan
{
dfn[x]=low[x]=++d,vis[S[++T]=x]=1;
for(RI i=lnk[x];i;i=e[i].nxt)
!dfn[e[i].to]?(dfs(e[i].to,x),Gmin(low[x],low[e[i].to]))
:vis[e[i].to]&&Gmin(low[x],dfn[e[i].to]);
if(dfn[x]^low[x]) return;col[x]=++cnt,vis[x]=0;
W(S[T]^x) col[S[T]]=cnt,vis[S[T--]]=0;--T;
}
I void Solve()
{
RI i;for(i=1;i<=2*n;++i) !dfn[i]&&(dfs(i,0),0);//Tarjan
for(i=1;i<=n;++i) if(col[i]==col[n+i]) return (void)puts("IMPOSSIBLE");//判无解
for(puts("POSSIBLE"),i=1;i<=n;++i) F.write(col[i]>col[n+i]," \n"[i==n]);//输出一组可行解
}
}
int main()
{
RI i,a,b,c,d;for(F.read(n),F.read(m),i=1;i<=m;++i)
F.read(a),F.read(b),F.read(c),F.read(d),add(a+n*(!b),c+n*d),add(c+n*(!d),a+n*b);//建边
return Tarjan::Solve(),F.clear(),0;
}

最新文章

  1. 如何利用python监控主机存活并邮件、短信通知
  2. AngularJs自定义指令--执行顺序 (原文:http://www.cnblogs.com/sagacite/p/4624227.html)
  3. 《理解 ES6》阅读整理:函数(Functions)(二)Unnamed Parameters
  4. ubuntu14.04 LTS 更新源
  5. How to: Write Object Data to an XML File
  6. git和其他版本控制系统的区别
  7. github/python/ show me the code 25题(二)
  8. codility上的练习 (1)
  9. ps遇到的问题及笔记
  10. Ambari大数据的管理利器
  11. Purge and Seal Test on 09 GMC Yukon with Autel Maxisys Pro MS908P scanner
  12. 基于vs2015的rdlc报表运行环境部署
  13. Oracle 同义词(Synonym)
  14. Confluence 6 配置验证码(Captcha)来防止垃圾
  15. JS平滑无缝滚动实现———实现首页广告自动滚动效果(附实例)
  16. javaScript——原型继承四步曲
  17. syslog-ng应用详解
  18. iuplua test failure
  19. EditText: android:focusable和android:focusableInTouchMode的区别
  20. DB2开发系列之二——SQL过程

热门文章

  1. WebSocket断开原因、心跳机制防止自动断开连接
  2. SSM + VUE 实现简单的 CRUD
  3. 批发市场收记账管理系统(iPad与手机版)水产批发市场客户欠账、还款管理水产宝介绍 第八章 财务(应收账款,应付账款,已收账款,已付账款)
  4. mongodb使用_遍历列表中的元素,作为变量,循环修改mongodb中的字段
  5. python 基础学习笔记(6)--函数(1)
  6. [Go]TCP服务中增加消息队列与工作池
  7. 斐波那契数列(Java)
  8. IDEA快捷键用法
  9. python大作业二
  10. dotnet core 调用electron来开发UI的探索