D35【模板】2-SAT
SAT 是适定性(Satisfiability)问题的简称.一般形式为 k-适定性问题,简称 k-SAT.而当 𝑘 >2
时该问题为 NP 完全的.所以只研究 𝑘=2
的情况.
2-SAT,给 𝑛
个布尔变量 𝑥1∼𝑥𝑛
,𝑚
个需要满足的条件,每个条件的形式都是 𝑥𝑖
为 1/0 或 𝑥𝑗
为 1/0.给每个变量赋值使得所有条件得到满足. 输出一种可行方案即可.
设 𝑎
表示 𝑥𝑎
为真(¬𝑎
就表示 𝑥𝑎
为假).如果有个人提出的要求分别是 𝑎
和 𝑏
,即 (𝑎∨𝑏)
(变量 𝑎,𝑏
至少满足一个).对这些变量关系建有向图,则把 𝑎
成立或不成立用图中的点表示,¬𝑎 →𝑏
且 ¬𝑏 →𝑎
,表示 𝑎
不成立 则 𝑏
一定成立;同理,𝑏
不成立 则 𝑎
一定成立.
若两点在同一强连通分量内,则这两点代表的条件 要么都满足,要么都不满足.
建图后使用 Tarjan 算法找 SCC,判断对于任意布尔变量 𝑎
,表示 𝑎
成立的点和表示 𝑎
不成立的点是否在同一个 SCC 中,若有则输出无解,否则有解.
如果变量 𝑥
的拓扑序在 ¬𝑥
之后,那么取 𝑥
值为真.应用到 Tarjan 算法的缩点,即 𝑥
所在 SCC 编号在 ¬𝑥
之前时,取 𝑥
为真.因为 Tarjan 算法求强连通分量时使用了栈,如果跑完 Tarjan 缩点之后呈现出的拓扑序更大,在 Tarjan 会更晚被遍历到,就会更早地被弹出栈而缩点,分量编号会更小,所以 Tarjan 求得的 SCC 编号相当于 反拓扑序.
算法会把整张图遍历一遍,由于这张图 𝑛
和 𝑚
同阶,计算答案时复杂度为 𝑂(𝑛)
,因此总复杂度为 𝑂(𝑛)
.

D14 强连通分量 Tarjan 算法 - 董晓 - 博客园
P4782 【模板】2-SAT - 洛谷// 2-SAT+tarjan O(n+m) #include<bits/stdc++.h> using namespace std; const int N=2000005; int n,m; int hd[N],to[N],ne[N],idx; int dfn[N],low[N],tim,stk[N],top,scc[N],cnt; void add(int a,int b){ to[++idx]=b,ne[idx]=hd[a],hd[a]=idx; } void tarjan(int x){ dfn[x]=low[x]=++tim; stk[++top]=x; for(int i=hd[x];i;i=ne[i]){ int y=to[i]; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(!scc[y]) low[x]=min(low[x],dfn[y]); } if(low[x]==dfn[x]){ ++cnt; for(int y=-1;y!=x;) scc[y=stk[top--]]=cnt; } } int main(){ scanf("%d%d",&n,&m); for(int i,a,j,b;m--;){ scanf("%d%d%d%d",&i,&a,&j,&b); add(i+!a*n,j+b*n); //xi拆成i和i+n add(j+!b*n,i+a*n); } for(int i=1;i<=2*n;i++)if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++)if(scc[i]==scc[i+n]){ puts("IMPOSSIBLE"); return 0; } puts("POSSIBLE"); for(int i=1;i<=n;i++)printf("%d ",scc[i]>scc[i+n]); return 0; }
P4171 [JSOI2010] 满汉全席 - 洛谷
#include<bits/stdc++.h> using namespace std; const int N=205; int t,n,m; int head[N],idx; struct Edge{int to,ne;}e[4005]; int dfn[N],low[N],tim,stk[N],top,scc[N],cnt; char s1[5],s2[5]; void add(int a,int b){ e[++idx].to=b; e[idx].ne=head[a]; head[a]=idx; } void tarjan(int x){ dfn[x]=low[x]=++tim; stk[++top]=x; for(int i=head[x];i;i=e[i].ne){ int y=e[i].to; if(!dfn[y]){ //若y尚未访问 tarjan(y); low[x]=min(low[x],low[y]); } else if(!scc[y]) //若y已访问且未处理 low[x]=min(low[x],dfn[y]); } if(low[x]==dfn[x]){ //若x是SCC的根 ++cnt; for(int y=-1;y!=x;) scc[y=stk[top--]]=cnt; } } int main(){ scanf("%d",&t); while(t--){ idx=tim=cnt=top=0; memset(head,0,sizeof head); memset(dfn,0,sizeof dfn); memset(scc,0,sizeof scc); scanf("%d%d",&n,&m); while(m--){ scanf("%s%s",&s1,&s2); int i=0,j=0,a,b,k; a=(s1[0]=='m'?0:1); b=(s2[0]=='m'?0:1); for(k=1;s1[k]>='0'&&s1[k]<='9';) i=i*10+s1[k++]-'0'; for(k=1;s2[k]>='0'&&s2[k]<='9';) j=j*10+s2[k++]-'0'; add(i+n*!a,j+n*b); add(j+n*!b,i+n*a); } for(int i=1;i<=n<<1;++i)if(!dfn[i])tarjan(i); bool flag=0; for(int i=1;i<=n;++i) if(scc[i]==scc[i+n]){ flag=1; break; } flag?puts("BAD"):puts("GOOD"); } }
浙公网安备 33010602011771号