본문 바로가기

Dev/BOJ

[백준 11280] 2-SAT - 3

 

 그래프 이론을 이용해 푸는 2-SAT 문제. 처음 풀어보는 유형의 문제라, 알고 있는 알고리즘들을 다양하게 떠올려 풀어보려 했지만 마땅한 로직을 찾아내기 힘들었다. 논리식을 유심히 관찰했는데, 여러 가지 관계를 떠올릴 순 있었으나 제 시간 내에 해결할 수 있는 알고리즘으로 잘 연결되지 않았다.

 

 그래서 한참 고민 하다가, 관련된 내용을 찾아봤더니 SCC(Strongly Connected Component)를 이용해서 해결하는 어느정도 정형화된 로직이 있는 것 같았다. 읽으면서 처음 이렇게 접근할 생각을 한 사람이 누구일까 싶었다. 그리고 SCC를 빠르게 찾아내는 알고리즘들도 함께 찾아봤는데, 이건 그래프 문제들을 풀 때 정말 도움이 많이 될 것 같았다.

 

 일단 논리식으로부터 접근하는데, 문제의 각 절들이 모두 true가 되어야 한다는 사실은 누구나 관찰할 수 있을 것이다. 그리고 각 절이 true가 되기 위해서는 당연하게도 두 변수 중 한 변수는 true가 되어야 한다. 이는 둘 중 한 변수가 false인 경우, 나머지 한 변수는 true로 확정해야 한다는 소리이고 이를 그래프로 나타내볼 수 있다.

 만약에 절 하나가 (x V y)라면 ~x -> y 와 ~y -> x로 생각할 수 있다. 이렇게 논리식에서 변수들의 요구 조건을 그래프로 나타낸 것이 함축 그래프라고 한다. 모든 절에 대해서, 함축 그래프를 완성해 주고 SCC를 구해준다. 

 이 때, 어떤 변수 x와 그 역인 ~x가 같은 SCC에 속해있다면, x->~x인 경로와 ~x->x인 경로가 모두 연결 요소 내에 존재한다는 소리가 된다. 이는 x와 ~x가 모두 true가 되어야 함을 의미하며, 이는 모순으로 불가능하다. 따라서 SCC를 모두 구한 뒤에 변수와 그 변수의 역이 한 SCC에 속한 경우가 있다면, 식 f는 false가 되며 그 외의 경우엔 true가 된다.

 

 SCC를 구하는 알고리즘은 코사라주 알고리즘을 사용했다. 일단 그쪽이 구현이 간편해서 사용했는데, 다음에 SCC를 구하는 문제가 또 나오면 타잔 알고리즘도 사용해 봐야겠다.

#include <iostream>
#include <vector>
#include <unordered_set>
#include <stack>

using namespace std;

int n, m;
vector<unordered_set<int> > cnfGraph;
vector<unordered_set<int> > revCnfGraph; 
stack<int> st;
vector<bool> isVisited;
vector<int> scc;
int sccInd = 0;

void dfs(int x) {
    if(isVisited[x]) return;
    isVisited[x] = true;
    for(int adj: cnfGraph[x]) {
        if(!isVisited[adj]) {
            dfs(adj);
        }
    }
    st.push(x);
}

void reDfs(int x) {
    if(isVisited[x]) return;
    isVisited[x] = true;
    scc[x] = sccInd;
    for(int adj: revCnfGraph[x]) {
        if(!isVisited[adj]) {
            reDfs(adj);
        }
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n >> m;
    cnfGraph = vector<unordered_set<int> >(2*n+1);
    revCnfGraph = vector<unordered_set<int> >(2*n+1);
    isVisited = vector<bool>(n*2+1, false);
    scc = vector<int>(n*2+1);

    for(int i=0; i<m; i++) {
        int u, v;
        cin >> u >> v;

        cnfGraph[n-u].insert(v+n);
        cnfGraph[n-v].insert(u+n);
        revCnfGraph[v+n].insert(n-u);
        revCnfGraph[u+n].insert(n-v);
    }

    for(int i=0; i<n; i++) {
        dfs(i);
    }
    for(int i=n+1; i<=n*2; i++) {
        dfs(i);
    }

    isVisited = vector<bool>(n*2+1, false);

    while(!st.empty()) {
        auto cur = st.top();
        st.pop(); 
        if(!isVisited[cur]) {
            sccInd++;
            reDfs(cur);
        }
    }

    for(int i=1; i<=n; i++) {
        if(scc[n-i] == scc[n+i]) {
            cout << "0";
            return 0;
        }   
    }

    cout << "1";
}

 

'Dev > BOJ' 카테고리의 다른 글

[백준 7569] 토마토  (1) 2024.10.25
[백준 2150] Strongly Connected Component  (0) 2024.10.24
[백준 14725] 개미굴  (3) 2024.10.21
[백준 14938] 서강그라운드  (2) 2024.10.17
[백준 11266] 단절점  (4) 2024.10.16