D35【模板】2-SAT

董晓發表於2024-08-02

影片連結:D35【模板】2-SAT_嗶哩嗶哩_bilibili

D14 強連通分量 Tarjan 演算法 - 董曉 - 部落格園 (cnblogs.com)

P4782 【模板】2-SAT - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)

// 2-SAT+tarjan O(n+m)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=2000005;
int n,m;
int head[N],to[N],ne[N],idx;
int dfn[N],low[N],tim,stk[N],top,scc[N],cnt;

void add(int a,int b){
  to[++idx]=b,ne[idx]=head[a],head[a]=idx;
}
void tarjan(int x){
  dfn[x]=low[x]=++tim;
  stk[++top]=x;
  for(int i=head[x];i;i=ne[i]){
    int y=to[i];
    if(!dfn[y]){ //若y尚未訪問
      tarjan(y);
      low[x]=min(low[x],low[y]);
    }
    else if(!scc[y]) //若y已訪問且未處理
      low[x]=min(low[x],dfn[y]);
  }
  
  if(low[x]==dfn[x]){ //若x是SCC的根
    ++cnt;
    for(int y=-1;y!=x;) scc[y=stk[top--]]=cnt;
  }
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i,a,j,b;m--;){
    scanf("%d%d%d%d",&i,&a,&j,&b);
    add(i+!a*n,j+b*n); //xi拆成i和i+n
    add(j+!b*n,i+a*n);
  }
  
  for(int i=1;i<=2*n;i++)
    if(!dfn[i]) tarjan(i); //求SCC
  for(int i=1;i<=n;i++)
    if(scc[i]==scc[i+n]){
      puts("IMPOSSIBLE");
      return 0;
    }
  puts("POSSIBLE");
  for(int i=1;i<=n;i++)
    printf("%d ",scc[i]>scc[i+n]);
}

// 2-SAT+tarjan O(n+m)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=2000005;
int n,m;
int head[N],to[N],ne[N],idx;
int dfn[N],low[N],tim,stk[N],top,scc[N],cnt;

void add(int a,int b){
  to[++idx]=b,ne[idx]=head[a],head[a]=idx;
}
void tarjan(int x){
  dfn[x]=low[x]=++tim;
  stk[++top]=x;
  for(int i=head[x];i;i=ne[i]){
    int y=to[i];
    if(!dfn[y]){ //若y尚未訪問
      tarjan(y);
      low[x]=min(low[x],low[y]);
    }
    else if(!scc[y]) //若y已訪問且未處理
      low[x]=min(low[x],dfn[y]);
  }
  
  if(low[x]==dfn[x]){ //若x是SCC的根
    ++cnt;
    for(int y=-1;y!=x;) scc[y=stk[top--]]=cnt;
  }
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i,a,j,b;m--;){
    scanf("%d%d%d%d",&i,&a,&j,&b);
    i--,j--;
    add(2*i+!a,2*j+b); //xi拆成2i和2i+1
    add(2*j+!b,2*i+a);
  }
  
  for(int i=0;i<2*n;i++)
    if(!dfn[i]) tarjan(i); //求SCC
  for(int i=0;i<2*n;i+=2)
    if(scc[i]==scc[i+1]){
      puts("IMPOSSIBLE");
      return 0;
    }
  puts("POSSIBLE");
  for(int i=0;i<2*n;i+=2)
    printf("%d ",scc[i]>scc[i+1]);
}

板子題:P4171 [JSOI2010] 滿漢全席 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=205;
int t,n,m;
int head[N],idx;
struct Edge{int to,ne;}e[4005];
int dfn[N],low[N],tim,stk[N],top,scc[N],cnt;
char s1[5],s2[5];
 
void add(int a,int b){
  e[++idx].to=b;
  e[idx].ne=head[a];
  head[a]=idx;
}
void tarjan(int x){
  dfn[x]=low[x]=++tim;
  stk[++top]=x;
  for(int i=head[x];i;i=e[i].ne){
    int y=e[i].to;
    if(!dfn[y]){ //若y尚未訪問
      tarjan(y);
      low[x]=min(low[x],low[y]);
    }
    else if(!scc[y]) //若y已訪問且未處理
      low[x]=min(low[x],dfn[y]);
  }
  
  if(low[x]==dfn[x]){ //若x是SCC的根
    ++cnt;
    for(int y=-1;y!=x;) scc[y=stk[top--]]=cnt;
  }
}
int main(){
  scanf("%d",&t);
  while(t--){
    idx=tim=cnt=top=0;
    memset(head,0,sizeof head);
    memset(dfn,0,sizeof dfn);    
    memset(scc,0,sizeof scc);
    scanf("%d%d",&n,&m);
    while(m--){
      scanf("%s%s",&s1,&s2);
      int i=0,j=0,a,b,k;
      a=(s1[0]=='m'?0:1);
      b=(s2[0]=='m'?0:1);
      for(k=1;s1[k]>='0'&&s1[k]<='9';)
        i=i*10+s1[k++]-'0';
      for(k=1;s2[k]>='0'&&s2[k]<='9';)
        j=j*10+s2[k++]-'0';
      add(i+n*!a,j+n*b); 
      add(j+n*!b,i+n*a);
    }
    
    for(int i=1;i<=n<<1;++i)if(!dfn[i])tarjan(i);
    bool flag=0;
    for(int i=1;i<=n;++i)
      if(scc[i]==scc[i+n]){
        flag=1; break;
      }
    flag?puts("BAD"):puts("GOOD");
  }
}

練習題:

Catowice City - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)

P3007 [USACO11JAN] The Continental Cowngress G - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)

Ring Road 2 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)