花了几天时间肝了几道2-SAT的题。
越做题越会发现,这个东西挺模板的了解原理之后只要会建边基本上就做出来了(除非加什么花里胡哨的二分)。
这篇博客写一个Seoul区域赛场的一个2-SAT问题。
读者可以去codeforces.com的gym搜seoul然后倒数第二题就是这道题了。
首先我们捋一下2-SAT解决问题流程:
其实2-SAT细究的话,也就是个 Tarjan求强连通分量+缩点反向建边+拓扑排序+染色
1. 根据约束条件建边
2. tarjan求出强连通分量,并且对于同一个强连通分量中的点编同一个号。
3. 判断是否有同一个集合中的两个点出现在同一个强连通分量中,如果有那么无解,如果没,那么就一定存在解。
4. 缩点,把一个强连通分量视为一个点,根据我们步骤2中对强连通分量编的号缩点。
5. 对于缩点之后,我们将边反向,建立新图。
6. 拓扑排序,如果未染色就对新图结点染色。
7. 输出结果,对于所有集合,同一输出颜色相同的点。
当我们做过几个题后,如果不是花里胡哨的题目的话我们可以很容易的发现,建边是整个题的灵魂,建好边就成功了百分之80。然后改输出就完成了百分之90。模板别抄错就AC了
接下来我们来说一些建边的技巧:
1. 如果选 A 必须 不选 B, 那么建立 A - > !B ,和 B --> !A 的两条边
2. 如果选 A 必须选 B,那么建立 A -->B 的 ,和 !B -->!A的两个边
3. 无论如何必须选A ,建立 !A --- > A 的边(方向千万不要弄反)
说白了, -----> 这个箭头的意思就是离散数学中的 “蕴含”,就是选了左边的就一定要选右边的。
接下来说题目大意 :
这里有N鸽灯泡,每种灯泡可能有两种颜色,R是红色,B是绿色。
然后有m位老鸽来猜灯泡的颜色,规定一个人只能描述三个灯泡的颜色,而且至少说对两个灯泡。
问你有无可行解满足题意。
可以对n鸽灯泡的两种状态作为2-SAT问题中的一个集合中的两个元素。那么比如说1号灯泡(为了建图编号方便,我们对这个一号灯泡编号减一,下边就说0号灯泡了) ,我们对他有两个结点,2* i 表示他是红色,2*i+1表示蓝色。
接下来我们建边,对于每一位老鸽,他说的至少有两个对的,我们来找隐含的而约束条件如果他猜 1R 2B 3B,是不是意思就是如果第一个灯泡是蓝色,那么其他两个他说的一定是对的,也就是说 “1号灯泡蓝色 ---> 2号灯泡蓝色,3号灯泡蓝色” ,对于他猜的二号灯泡同理建边,所以对于一位老鸽来说,要建立6跳边,然后抄模板,输出,AC。
代码(我这里建边比较辣鸡,写的有点长,读者完全可以按小编是弱智处理):
#include<iostream>
#include<string.h>
#include<algorithm>
#include<vector>
#include<stdio.h>
#include<cmath>
#include<string>
#include<queue>
#include<stack>
#include<queue>
#define ll long long
using namespace std;
const ll MAXN=1e4+5;
const ll MAXM=6e6+5;
int n,m,dfn[MAXN],tot,low[MAXN],belong[MAXN],head[MAXN];
int cnt,col,tot2;
int cnt2,head2[MAXN],in[MAXN],color[MAXN],cp[MAXN];
bool Instack[MAXN];
struct edge
{
int from,to,next;
}Edge[MAXM],Edge2[MAXM];
struct Time
{
int val;
int x,y;
}C[MAXN];
stack<int>S;
void add(int x,int y)
{
Edge[++tot].to=y;Edge[tot].from=x;
Edge[tot].next=head[x];head[x]=tot;
}
void add2(int x,int y)
{
Edge2[++tot2].to=y;Edge2[tot2].from=x;
Edge2[tot2].next=head2[x];head2[x]=tot2;
}
void tarjan(int x)
{
dfn[x]=low[x]=++cnt;
S.push(x);
Instack[x]=1;
for(int i=head[x];i!=-1;i=Edge[i].next){
int to=Edge[i].to;
if(!dfn[to]){
tarjan(to);
low[x]=min(low[to],low[x]);
}
else if(Instack[to]){
low[x]=min(low[x],dfn[to]);
}
}
if(dfn[x]==low[x]){
Instack[x]=0;
belong[x]=++col;
while(S.top()!=x){
belong[S.top()]=col;
Instack[S.top()]=0;
S.pop();
}
S.pop();
}
}
int main()
{
memset(head,-1,sizeof(head));
memset(head2,-1,sizeof(head2));
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int a,b,c;
char cc,d,e;
cin>>a>>cc>>b>>d>>c>>e;a--,b--,c--;
if(cc=='R'){
if(d=='R'){
add(2*a+1,2*b);
}
else if(d=='B'){
add(2*a+1,2*b+1);
}
if(e=='R'){
add(2*a+1,2*c);
}
else if(e=='B'){
add(2*a+1,2*c+1);
}
}
else {
if(d=='R'){
add(2*a,2*b);
}
else if(d=='B'){
add(2*a,2*b+1);
}
if(e=='R'){
add(2*a,2*c);
}
else if(e=='B'){
add(2*a,2*c+1);
}
}
if(d=='R'){
if(cc=='R'){
add(2*b+1,2*a);
}
else if(cc=='B'){
add(2*b+1,2*a+1);
}
if(e=='R'){
add(2*b+1,2*c);
}
else if(e=='B'){
add(2*b+1,2*c+1);
}
}
else {
if(cc=='R'){
add(2*b,2*a);
}
else if(cc=='B'){
add(2*b,2*a+1);
}
if(e=='R'){
add(2*b,2*c);
}
else if(e=='B'){
add(2*b,2*c+1);
}
}
if(e=='R'){
if(cc=='R'){
add(2*c+1,2*a);
}
else if(cc=='B'){
add(2*c+1,2*a+1);
}
if(d=='R'){
add(2*c+1,2*b);
}
else if(d=='B'){
add(2*c+1,2*b+1);
}
}
else {
if(cc=='R'){
add(2*c,2*a);
}
else if(cc=='B'){
add(2*c,2*a+1);
}
if(d=='R'){
add(2*c,2*b);
}
else if(d=='B'){
add(2*c,2*b+1);
}
}
}
while(S.size())S.pop();
for(int i=0;i<2*n;i++){
if(!dfn[i])tarjan(i);
//if(!dfn[2*i+1])tarjan(i*2+1);
}
bool flag=1;
for(int i=0;i<n;i++){//
if(belong[2*i]==belong[2*i+1]){
flag=0;break;
}
else{
cp[belong[2*i]]=belong[2*i+1];
cp[belong[2*i+1]]=belong[2*i];
}
}
memset(head2,-1,sizeof(head2));
for(int i=1;i<=tot;i++){//
if(belong[Edge[i].from]!=belong[Edge[i].to]){
add2(belong[Edge[i].to],belong[Edge[i].from]);
in[belong[Edge[i].from]]++;
}
}
queue<int>Q;
while(!Q.empty())Q.pop();
for(int i=1;i<=col;i++){//
if(in[i]==0)Q.push(i);
}
while(!Q.empty())
{
int tmp=Q.front();Q.pop();
if(color[tmp]==0){
color[tmp]=1;
color[cp[tmp]]=2;
}
for(int i=head2[tmp];i!=-1;i=Edge2[i].next){
int to=Edge2[i].to;//cout<<" * "<<to<<endl;
if(--in[to]==0)Q.push(to);
}
}
if (flag){
// printf("YES\n");
for(int i = 0; i < n; i++)
{
if(color[belong[2*i]] == 1)
printf("R");
else
printf("B");
}
printf("\n");
}
else printf("-1\n");
}
/*
5 6
1 B 3 R 4 B
2 B 3 R 4 R
1 B 2 R 3 R
3 R 4 B 5 B
3 B 4 B 5 B
1 R 2 R 4 R
*/