[백준] 11281번 2-SAT - 4 / Java, Python

Jini·2021년 9월 11일
1

백준

목록 보기
224/226

Baekjoon Online Judge

algorithm practice


- 단계별 문제풀기


37. 강한 연결 요소

Strongly connected component를 다뤄 봅시다.




6. 2-SAT - 4

11281번

2-SAT의 해를 출력해 봅시다.



2-SAT은 N개의 불리언 변수 (x_1, x_2, ..., x_n)가 있을 때, 2-CNF 식을 true로 만들기위해 (x_i)를 어떤 값으로 정해야하는지를 구하는 문제이다. 이번 문제는 변수의 개수 N과 절의 개수 M, 그리고 식 (f)가 주어졌을 때, 식 (f)를 true로 만들 수 있는지 없는지를 구하는 프로그램을 작성하는 문제이다.

SCC를 찾는 알고리즘은 크게 이 두가지 방법이다.

  • 타잔 알고리즘
    모든 정점에 대해 DFS를 수행하여 SCC를 찾는 알고리즘으로, 코사라주 알고리즘에 비해 적용이 더 쉽다고 한다.
    ① 인접 정점에 방문하며 자기 자신을 스택에 넣고, 재귀적으로 DFS를 수행한다.
    ② 인접 정점에 방문했지만, 아직 처리중인 상태일 경우, 작은 값으로 부모값을 갱신한다.
    ③ 부모 노드의 DFS가 끝난 경우에는, 자신의 id값이 스택에서 나올 때까지 스택에 있는 노드들을 pop하고 scc 배열에 추가한다.
    ④ 만들어진 하나의 scc를 전체 SCC 배열에 추가한다.
    (구현이 더 어렵지만, 활용도는 더 높다고 한다.)
  • 코사라주 알고리즘
    주어진 방향 그래프의 역방향 그래프와 스택을 사용하여 SCC를 구하는 알고리즘이다. 방향, 역방향 그래프가 동일한 SCC를 구성한다는 것을 이용한 방법이다.
    ① 주어지는 방향 그래프와 그 그래프의 역방향 그래프를 만든다.
    ② 정점을 담을 스택을 만들고 임의의 정점부터 DFS를 수행한다.
    ③ DFS가 끝나는 순서대로 스택에 삽입하고, 아직 방문하지 않은 정점에 대해 DFS를 수행한다.
    ④ 모든 정점이 스택에 담긴 후에는 스택을 pop하여 나오는 정점부터 역방향 그래프에서 DFS를 수행한다.. 이미 방문한 정점은 pop만 시행한다.
    ⑤ 이때 탐색되는 모든 정점을 SCC로 묶는다.
    이것을 스택이 빌 때까지 진행한다.
    (타잔 알고리즘에 비해 구현이 더 쉬운 편이라고 한다.)

저번 문제들과 유사하고, scc 응용 문제이다.
타잔 알고리즘을 이용했다..!



Java / Python


  • Java

import java.io.*;
import java.util.*;

public class Main {
	static int N, V, num;
	static ArrayList<ArrayList<Integer>> graph, scc_arr;
	static int[] parent, compare, CNF;
	static boolean[] visit; // SCC 확정된 정점 확인
	static Stack<Integer> stack;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		StringTokenizer st = new StringTokenizer(br.readLine());

		N = Integer.parseInt(st.nextToken());
		int M = Integer.parseInt(st.nextToken());

		parent = new int[2 * N + 1];
		compare = new int[2 * N + 1];
		visit = new boolean[2 * N + 1];
		stack = new Stack<>();
		num = 0;
		V = 0;
		CNF = new int[2 * N + 1];
		Arrays.fill(CNF, -1);
		
		graph = new ArrayList<>();
		scc_arr = new ArrayList<>();
		for (int i = 0; i < 2 * N + 1; i++) {
			graph.add(new ArrayList<>());
		}

		while (M-- > 0) {
			st = new StringTokenizer(br.readLine());

			int u = Integer.parseInt(st.nextToken());
			int v = Integer.parseInt(st.nextToken());

			graph.get(validate(-u)).add(validate(v));
			graph.get(validate(-v)).add(validate(u));
		}

		for (int i = 1; i < 2 * N + 1; i++) {
			if (!visit[i]) {
				SCC(i);
			}
		}
		int sts = 1;
		for (int i = 1; i < N + 1; i++) {
			if (compare[i] == compare[i + N])
				sts = 0;
		}

		bw.write(sts + "\n");
		if (sts == 1)
			bw.write(setCNF() + "\n");

		bw.flush();
		bw.close();
		br.close();
	}

	private static int validate(int n) {
		return (0 < n && n < N + 1) ? n : -n + N;
	}

	private static String setCNF() {
		for (int i = V - 1; i > -1; i--) {
			for (int j : scc_arr.get(i)) {
				int now = Math.abs(validate(j));
				if (CNF[now] == -1) {
					if (j > N) {
						CNF[now] = 1;
					} else {
						CNF[now] = 0;
					}
				}
			}
		}
		StringBuilder sb = new StringBuilder();
		for (int i = 1; i < N + 1; i++) {
			sb.append(CNF[i]).append(' ');
		}
		return sb.toString();
	}

	private static int SCC(int idx) {
		parent[idx] = ++num;
		stack.push(idx);

		int root = parent[idx];
		for (int next : graph.get(idx)) {
			if (parent[next] == 0)
				root = Math.min(root, SCC(next));
			else if (!visit[next])
				root = Math.min(root, parent[next]);
		}

		if (root == parent[idx]) {
			ArrayList<Integer> tmp = new ArrayList<>();
			while (!stack.isEmpty()) {
				int top = stack.pop();
				tmp.add(top);
				visit[top] = true;
				compare[top] = V;

				if (top == idx)
					break;
			}
			V++;
			scc_arr.add(tmp);
		}
		return root;
	}
}




  • Python

import sys
sys.setrecursionlimit(10 ** 5)

N, M = map(int, sys.stdin.readline().split())
graph = [[] for _ in range(2 * N + 1)]

for _ in range(M):
    a, b = map(int, sys.stdin.readline().split())
    graph[-a].append(b)
    graph[-b].append(a)

scc_num = 1
idx = 1
stack = []
scc_idx = [0] * (2 * N + 1)
check = [0] * (2 * N + 1)
visit = [0] * (2 * N + 1)

def SCC(node):
    global idx, scc_num
    visit[node] = idx
    root = idx
    idx += 1
    stack.append(node)

    for nxt in graph[node]:
        if not visit[nxt]:
            root = min(root, SCC(nxt))
        elif not check[nxt]:
            root = min(root, visit[nxt])

    if root == visit[node]:
        while stack:
            top = stack.pop()
            check[top] = 1
            scc_idx[top] = scc_num
            if node == top:
                break

        scc_num += 1

    return root

for i in range(1, 2 * N + 1):
    if not visit[i]:
        SCC(i)

result = [0] * N
for i in range(1, N + 1):
    if scc_idx[i] == scc_idx[-i]:
        print(0)
        break
    if scc_idx[i] < scc_idx[-i]:
        result[i - 1] = 1
else:
    print(1)
    print(*result)





profile
병아리 개발자 https://jules-jc.tistory.com/

0개의 댓글