2-SAT
前置芝士:强连通分量。
先放一个板子题:2-SAT。
我们先考虑拆点,把每个变量
既然有了点,我们就要考虑连边。例如给一个条件:
然后看一下这条边表示什么,表示的是一个要求是另一个要求的充分条件(建议学习一下充分条件与必要条件)。
那么,如果发现图中出现了
换句话说,就是
说了这么多,我们终于说完了怎么判断有没有解,接下来说一下怎么构造解。
首先我们记
否则这两个东西的
那么,
这是一个小结论,证明的话建议看一些其他资料,这里建议记住,遇到的时候直接用就可以了。
下面放一下板子题的代码:
#include<bits/stdc++.h>
#define int long long
#define N 2000005
#define M 2000005
using namespace std;
int n,m,h[N],e[M],ne[M],idx,dfn[N],low[N],ts,stk[N],top,id[N],cnt;
bool ins[N];
void add(int a,int b){//链式前向星建边
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void tar(int u){//经典tarjan求强连通分量
dfn[u]=low[u]=++ts;
stk[++top]=u;
ins[u]=1;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(!dfn[j]){
tar(j);
low[u]=min(low[u],low[j]);
}
else if(ins[j])low[u]=min(low[u],dfn[j]);
}
if(low[u]==dfn[u]){
int y;
cnt++;
do{
y=stk[top--];
ins[y]=0;
id[y]=cnt;
}while(y!=u);
}
}
signed main(){
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--){
int i,a,j,b;
cin>>i>>a>>j>>b;
i--;j--;
add(2*i+!a,2*j+b);//按照上面说的充分条件的方式建边
add(2*j+!b,2*i+a);
}
for(int i=0;i<n*2;i++){
if(!dfn[i]){//tarjan
tar(i);
}
}
for(int i=0;i<n;i++){
if(id[i*2]==id[i*2+1]){//互为充分条件则无解
cout<<"IMPOSSIBLE";
return 0;
}
}
cout<<"POSSIBLE\n";//否则有解
for(int i=0;i<n;i++){
if(id[i*2]<id[i*2+1]){//"上司"具有最终决定权
cout<<"1 ";
}
else cout<<"0 ";
}
return 0;
}
再来一个稍微困难的题:满汉全席。
先把原题面进行转化,发现对于每个评审员要满足两个要求的至少一个。所以可以认为
我们对
这里把这道题进行略微强化,请读者思考如果
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】