题解 P4782 【【模板】2-SAT 问题】
题目链接:Link
Solution
将每一个点\(x_i\)拆成\(2i\)和\(2i+1\)(\(2i\)表示\(x_i\)的假状态,\(2i+1\)表示\(x_i\)的真状态),问题就化为了从2n个状态中选出原来的每一个\(x_i\)的一个状态。
这有什么用呢?别急,先举个栗子,“1 1 3 0”就可以用有向边 2->6 7->3 来表示,什么意思呢?2表示1为false,根据规则可得3一定为false(也就是6),7表示3为true,根据规则可得1一定为true。那么就可以对这个有向图的每个连通分量(不一定是强连通的)进行dfs处理,并加上剪枝、回溯就行了,细节见代码。
注意:随机数据时dfs 2-SAT跑得很快,但对于大规模的数据,最好还是使用Tarjan求2-SAT
// luogu-judger-enable-o2
#include<cstdio>
#include<vector>
using namespace std;
template<typename T> inline void read(T& t)
{
t=0; bool f=false; char ch;
while(ch=getchar(),!((ch>='0'&&ch<='9')||ch=='-'));
if(ch=='-') f=true,ch=getchar();
t=ch-'0';
while(ch=getchar(),ch>='0'&&ch<='9') t=t*10+ch-'0';
if(f) t=-t;
}
template<typename T,typename... Args> inline void read(T& t,Args&... args) { read(t); read(args...); }
const int maxn=1e6+7;
struct TwoSAT
{
int n;
vector<int> G[maxn*2];
bool mark[maxn*2];
int S[maxn*2],c;
bool dfs(int x)
{
if(mark[x^1]) return false;
if(mark[x]) return true;
mark[x]=true;
S[c++]=x;
for(int i=0;i<G[x].size();i++)
if(!dfs(G[x][i])) return false;
return true;
}
//x = xval or y = yval
void add_clause(int x,int xval,int y,int yval)
{
x=x*2+xval;
y=y*2+yval;
G[x^1].push_back(y);
G[y^1].push_back(x);
//一个不满足一定能推出另一个满足
}
bool solve()
{
for(int i=0;i<n*2;i+=2)
if(!mark[i]&&!mark[i+1])//没被访问过
{
c=0;
if(!dfs(i))
{
while(c>0) mark[S[--c]]=false;
//回溯
if(!dfs(i+1)) return false;
//true/false都不行那就无药可救了
}
}
return true;
}
}Ago;
int m,x,xval,y,yval;
int main()
{
#ifdef local
freopen("pro.in","r",stdin);
#endif
read(Ago.n,m);
while(m-->0)
{
read(x,xval,y,yval);
Ago.add_clause(x,xval,y,yval);
}
if(!Ago.solve()) printf("IMPOSSIBLE\n");
else
{
printf("POSSIBLE\n");
for(int i=1;i<=Ago.n;i++)
if(Ago.mark[i<<1]) printf("0 ");
else printf("1 ");
printf("\n");
}
return 0;
}
本作品由happyZYM采用知识共享 署名-非商业性使用-相同方式共享 4.0 (CC BY-NC-SA 4.0) 国际许可协议(镜像(简单版)镜像(完整版))进行许可。
转载请注明出处:https://www.cnblogs.com/happyZYM/p/11379603.html (近乎)全文转载而非引用的请在文首添加出处链接。