题解:
根据矛盾关系构造布尔公式。
令 0~n-1 表示妻子, n~2*n-1 表示丈夫
A1 C1
A2 C2
如果C1为0,C2为0,对应的布尔公式为 (A1+n)V(A2+n)
如果C1为0,C2为1,对应的布尔公式为 (A1+n)V(A2)
如果C1为1,C2为0,对应的布尔公式为 (A1)V(A2+n)
如果C1为1,C2为1,对应的布尔公式为 (A1)V(A2)
判断是否存在一组解,使得所有满足条件的合取范式 ((A1+n)V(A2+n))∧((A1+n)V(A2))∧((A1)V(A2+n))∧((A1)V(A2)) 的值为真;
如果存在,输出YES,反之,输出NO.
讲析取式转化成两个蕴含式,以此构图,然后进行强连通分量分解,求出所属强连通分量的拓扑序。
如果有 scc[i] == scc[n+i] 则无解,输出NO,反之,一定有解,输出YES。
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<cstring> 5 using namespace std; 6 #define pb push_back 7 #define mem(a,b) (memset(a,b,sizeof a)) 8 const int maxn=1e3+50; 9 10 int n,m; 11 int scc[2*maxn]; 12 bool vis[2*maxn]; 13 vector<int >vs; 14 vector<int >G[2*maxn],rG[2*maxn]; 15 void addEdge(int u,int v) 16 { 17 G[u].pb(v); 18 rG[v].pb(u); 19 } 20 void Dfs(int u) 21 { 22 vis[u]=true; 23 for(int i=0;i < G[u].size();++i) 24 { 25 int to=G[u][i]; 26 if(!vis[to]) 27 Dfs(to); 28 } 29 vs.pb(u); 30 } 31 void rDfs(int u,int k) 32 { 33 scc[u]=k; 34 vis[u]=true; 35 for(int i=0;i < rG[u].size();++i) 36 { 37 int to=rG[u][i]; 38 if(!vis[to]) 39 rDfs(to,k); 40 } 41 } 42 void SCC() 43 { 44 mem(vis,false); 45 vs.clear(); 46 for(int i=0;i < 2*n;++i) 47 if(!vis[i]) 48 Dfs(i); 49 int k=0; 50 mem(vis,false); 51 for(int i=vs.size()-1;i >= 0;--i) 52 { 53 int u=vs[i]; 54 if(!vis[u]) 55 rDfs(u,++k); 56 } 57 } 58 void Init() 59 { 60 for(int i=0;i < 2*maxn;++i) 61 G[i].clear(),rG[i].clear(); 62 } 63 int main() 64 { 65 while(~scanf("%d%d",&n,&m)) 66 { 67 Init(); 68 for(int i=1;i <= m;++i) 69 { 70 int a1,c1; 71 int a2,c2; 72 scanf("%d%d%d%d",&a1,&a2,&c1,&c2); 73 int x=a1+(c1 == 0 ? 1:0)*n;//矛盾关系对应的布尔公式为(xVy),转化成两个蕴含式即为有向图中对应的边 74 int y=a2+(c2 == 0 ? 1:0)*n; 75 addEdge((x >= n ? x-n:x+n),y); 76 addEdge((y >= n ? y-n:y+n),x); 77 } 78 SCC(); 79 bool flag=false; 80 for(int i=0;i < n;++i) 81 if(scc[i] == scc[i+n]) 82 flag=true; 83 if(flag) 84 printf("NO "); 85 else 86 printf("YES "); 87 } 88 }
分割线
从第一次学习2-sat,第一次AC这道题到今天,已经过去半年时间了;
这半年时间,成长了不少;
这两天,重新拾起2-sat的知识,理解上要透彻了些许,代码风格也变化了不少;
下面说说现在的我会对此题做如何解析:
假设ui,vi 分别代表第 i 对夫妇的妻子和丈夫,uj,vj 同理;
如果 ui 与 uj 矛盾,那么,构造的边就是:
ui -> vj , vj -> ui;
uj -> vi , vi -> uj;
跑一边强连通分量分解,判断ui和vi是否在同一个强连通分量中即可;
AC代码:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<vector> 5 using namespace std; 6 #define pb(x) push_back(x) 7 #define mem(a,b) memset(a,b,sizeof(a)) 8 const int maxn=(1e3+50); 9 10 int n,m; 11 int couple[maxn][2]; 12 int num; 13 int head[maxn<<1]; 14 struct Edge 15 { 16 int to; 17 int next; 18 }G[maxn*maxn<<1]; 19 void addEdge(int u,int v) 20 { 21 G[num]={v,head[u]}; 22 head[u]=num++; 23 } 24 struct SCC 25 { 26 int col[maxn<<1]; 27 bool vis[maxn<<1]; 28 vector<int >vs; 29 void DFS(int u) 30 { 31 vis[u]=true; 32 for(int i=head[u];~i;i=G[i].next) 33 { 34 int v=G[i].to; 35 if((i&1) || vis[v])//正向边,num为偶数 36 continue; 37 DFS(v); 38 } 39 vs.push_back(u);//拓扑序 40 } 41 void RDFS(int u,int k)//反向求解强连通分量 42 { 43 vis[u]=true; 44 col[u]=k; 45 for(int i=head[u];~i;i=G[i].next) 46 { 47 int v=G[i].to; 48 if(!(i&1) || vis[v])//反向边,num为奇数 49 continue; 50 RDFS(v,k); 51 } 52 } 53 int scc() 54 { 55 vs.clear(); 56 mem(vis,false); 57 for(int i=0;i < (n<<1);++i) 58 if(!vis[i]) 59 DFS(i); 60 61 int k=0; 62 mem(vis,false); 63 for(int i=vs.size()-1;i >= 0;--i)//从拓扑序的最大值开始查找SCC 64 if(!vis[vs[i]]) 65 RDFS(vs[i],++k); 66 return k;//强连通分量的个数 67 } 68 }_scc; 69 char *Solve() 70 { 71 _scc.scc(); 72 for(int i=0;i < n;++i) 73 if(_scc.col[couple[i][0]] == _scc.col[couple[i][1]]) 74 return "NO"; 75 return "YES"; 76 } 77 void Init() 78 { 79 num=0; 80 mem(head,-1); 81 } 82 int main() 83 { 84 // freopen("C:\Users\hyacinthLJP\Desktop\in&&out\contest","r",stdin); 85 while(~scanf("%d%d",&n,&m)) 86 { 87 Init();//不要忘啊 88 int cnt=0; 89 for(int i=0;i < n;++i) 90 { 91 couple[i][0]=cnt++;///妻子编号 92 couple[i][1]=cnt++;///丈夫编号 93 } 94 for(int i=1;i <= m;++i) 95 { 96 int a1,a2; 97 int c1,c2; 98 scanf("%d%d%d%d",&a1,&a2,&c1,&c2); 99 int u1=couple[a1][c1]; 100 int v1=couple[a1][!c1]; 101 int u2=couple[a2][c2]; 102 int v2=couple[a2][!c2]; 103 ///u1,u2不相容 104 addEdge(u1,v2); 105 addEdge(v2,u1);///反向边 106 addEdge(u2,v1); 107 addEdge(v1,u2);///反向边 108 } 109 printf("%s ",Solve()); 110 } 111 return 0; 112 }