sat问题
关于定义,详见赵爽的《2-SAT解法浅析》,大概意思是有n个bool变量,一个全集为所有的他们以及他们的非,取其中的若干个变量或其非组成或的表达式,将这些表达式相与,求使与出的值为真的解集,为n-sat问题。
可证明,n>2时为NP完全问题,所以只讨论n=2的情况。
对于一个表达式a+b, a+b=!(!a*!b) ,即,如果在解集中选择了a,那么必须选择!b,选择了b必然要选择!a
解法:构建包含2n个点的有向图,如果有a+b则在a和!b b和!a间连接一条边。如果a和!a在一个强连通分量中,则无解。要求解集,只需要将原图缩点后反向建图,然后染色,具体染色方法是将遇到的第一个没有颜色的点染成红色,与它矛盾的点染成蓝色,如此循环,所有的红色的点的集合就是解集。
poj2723 题意:有m层楼,每层楼有2把锁,有n组钥匙,每组2把,用的时候只能用一组中的一把(另一把不能再用),给出钥匙的分组,给出每层楼需要用哪两把钥匙开,开启楼层的时候按顺序从前往后依次来开,一层楼只需要一把钥匙就能打开,求最后能开多少把锁。
2sat+二分
一组的钥匙相当于原变量和非变量,一层楼的锁相当于元素的或,因为只需要一把钥匙就可以,所以为(a+b) 因为是从前往后开,只需要二分楼层就可以,根据楼层建图来判断是否满足题意。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<string>
#include<map>
#include<cmath>
using namespace std;
const int maxn=4200;
const int maxm=10010;
struct node{
int v,next;
}e[maxm];
int head[maxn],dfn[maxn],low[maxn],id[maxn],sta[maxn],match[maxn];//match数组表示在一个组的两把钥匙
int x[maxn],y[maxn],vis[maxn];
int n,m,cnt,top,indx,tot;
void add(int u,int v){
e[tot].v=v;
e[tot].next=head[u];
head[u]=tot++;
}
void tarjan(int u){ //tarjan缩点不解释
low[u]=dfn[u]=++indx;
sta[++top]=u;
vis[u]=1;
int p;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].v;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(!id[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
cnt++;
int p;
do{
p=sta[top];
id[p]=cnt;
}while(sta[top--]!=u);
}
}
int solve(int mid){ //mid前的楼层能打开,建图
indx=0;
top=-1;
cnt=tot=0;
memset(dfn,0,sizeof(dfn));
memset(id,0,sizeof(id));
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
for(int i=0;i<mid;i++){
add(x[i],match[y[i]]); //(a+b) a->!b b->!a
add(y[i],match[x[i]]);
}
for(int i=0;i<2*n;++i){
if(!dfn[i]) tarjan(i);
}
for(int i=0;i<n;++i)
if(id[i]==id[match[i]]) return false; // 两个点在同一个强连通分量,则无法打开
return true;
}
int main()
{
int u,v;
ios::sync_with_stdio(0);
while(cin>>n>>m,m+n){
for(int i=0;i<n;++i){
cin>>u>>v;
match[u]=v; //a !a
match[v]=u;
}
for(int i=0;i<m;++i)
cin>>x[i]>>y[i];
int l=0,r=m+1,mid;
while(l+1<r){ //二分
mid=(l+r)/2;
if(solve(mid)) l=mid;
else r=mid;
}
cout<<l<<endl;
}
return 0;
}