2—SAT建图就是如果a与b矛盾(如果选择a就一定选择b'),建边a—>b';
好题啊!!!!2—SAT+二分,,
做过几道这样的题了,这道题建图有点难,
通过这道题让我对2—SAT的矛盾关系有了更深的理解了,之前做过的几道这样的题:
hdu 3622 :如果两个炸弹a,b的距离<d,就是a与b矛盾,所以建边a—>b';
hdu 1815(poj 2749):如果两个牛棚连接同一中转站的距离>d,就是a与b,a'与b'矛盾
如果两个牛棚连接不同中转站的距离>d,就是a与b',a'与b矛盾
hdu 3715:如果c[i]=0:a'(x[a[i]]=0)与b'(x[b[i]]=0)矛盾
如果c[i]=1;a'与b(x[b[i]]=1)矛盾,a与b'矛盾
如果c[i]=2:a与b矛盾
此题刚开始建图时,就认为,每对钥匙选一个就是a与b矛盾,
每个门上的两个锁选一个就是a与b矛盾,建边a—>b',果断wrong。
想了好久,发现这样建边根本不符合2—SAT,因为一个门上的两个锁并不是选择开这一个另一个一定不选择的关系,
也就是a与b并不是只能选其一的关系,这题应该是a'与b'矛盾(如果不选a,一定要选b)
所以建边a'—>b,b'—>a;
构图(矛盾)思路:
(1)A and B = 0 添加弧 A->!B , B->!A
2)A and B = 1 !A->A , !B->B
(3)A or B = 0 A->!A , B->!B
#include<stdio.h> #include<stack> #include<string.h> #define N 5000 using namespace std; struct edge { int ed,next; }E[20000]; struct op { int key1,key2; }Key[N]; struct ep { int lock1, lock2; }Door[N]; struct eg { int ed; eg *next; }*e[N]; int n,m,num,idx,ans,low[N],dfs[N],belong[N],ins[N],first[N]; void addedge(int x,int y) { E[num].ed=y; E[num].next=first[x]; first[x]=num++; } void addeg(int x,int y) { eg *p=new eg; p->ed=y; p->next=e[x]; e[x]=p; } void insit() { memset(dfs,-1,sizeof(dfs)); memset(low,0,sizeof(low)); memset(ins,0,sizeof(ins)); memset(belong,-1,sizeof(belong)); memset(first,-1,sizeof(first)); idx=ans=0;num=0; } stack<int>Q; void Tarjan(int x) { int v,p; low[x]=dfs[x]=idx++; ins[x]=1; Q.push(x); for(p=first[x];p!=-1;p=E[p].next) { v=E[p].ed; if(dfs[v]==-1) { Tarjan(v); low[x]=low[x]>low[v]?low[v]:low[x]; } else if(ins[v]==1) { low[x]=low[x]>dfs[v]?dfs[v]:low[x]; } } if(low[x]==dfs[x]) { do { v=Q.top(); Q.pop(); ins[v]=0; belong[v]=ans; }while(v!=x); ans++; } } int judge(int d) { int i,x,y; insit(); for(i=0;i<d;i++)//+2*n表示不选, { x=Door[i].lock1; y=Door[i].lock2; addedge(x,y+2*n); addedge(y,x+2*n); } for(i=0;i<n;i++) { x=Key[i].key1; y=Key[i].key2; addedge(x+2*n,y); addedge(y+2*n,x); } for(i=0;i<n*4;i++) { if(dfs[i]==-1) Tarjan(i); } for(i=0;i<2*n;i++) { if(belong[i]==belong[i+2*n]) return 0; } return 1; } int main() { int min,max,mid,i,mmax; while(scanf("%d%d",&n,&m),n||m) { for(i=0;i<n;i++) { scanf("%d%d",&Key[i].key1,&Key[i].key2); addeg(Key[i].key1,Key[i].key2); addeg(Key[i].key2,Key[i].key2); } for(i=0;i<m;i++) scanf("%d%d",&Door[i]. lock1,&Door[i]. lock2); min=0;max=m; mmax=0; while(min<=max) { mid=(min+max)/2; if(judge(mid)) { mmax=mid; min=mid+1; } else max=mid-1; } printf("%d\n",mmax); } return 0; }