레야몬
[C++] 11280번 2-SAT - 3 - 그래프 이론, 강한 연결 요소, 2-sat 본문
2-SAT - 3이랑 2-SAT - 4랑 한꺼번에 풀어서 함께 풀이해보려고 한다.
1. 문제
- 변수의 개수 N과 절의 개수 M, 그리고 식 f가 주어졌을 때, 식 f를 true로 만들 수 있는지 없는지를 구하는 프로그램을 작성하라. (있으면 그 해를 출력하라 <2-SAT - 4>)
- \(N(1 \leq N \leq 10,000)\), \(M(1 \leq M \leq, 100,000)\)
- <입력>
- N과 M
- 절 \((1 \leq, \left|i \right|, \left|i \right| \leq N)\) i, j가 양수인 경우 xi, xj를 음수인 경우는 ㄱx-i, ㄱx-j를 나타낸다.
- <출력
- 식 f를 true로 만들 수 있으면 1을, 없으면 0을 출력하라. (식 f가 true가 될 수 있을 경우 그 해를 출력하라. <2-SAT - 4>)
2. 문제 재정의
- 식에 true가 될 수 있는 xk가 있는지 판단하라. (있으면 그 해를 구하라 <2-SAT - 4>)
3. 해결 방법
- a, b를 받자. xa -> 2*a-1, !xa -> 2*a으로 하고 !a -> b와 !b -> a를 그래프에 추가하자
- ex) Vertex[2*a].push_back(2*b-1)
- 그래프를 생성하고 나서 타잔 알고리즘을 이용해 강한 연결 요소 끼리 묶어보자.
- 타잔 알고리즘으로 탐색한 강한 연결 요소 각각에 번호를 매겨주면 SCC_Count[n*2-1] == SCC_Count[n*2]를 통해 같으면 불가능, 다르면 가능하다는 것을 알 수 있다.
- 가능할 경우 타잔의 탐색 순서로 노드를 정리해주자. 타잔의 역순으로 위상 정렬이 되기 때문에 a -> !a의 경우 !a가 참이되어야 하므로 위상 정렬의 역순으로 값을 설정해주어야 한다.
- isTrue[i]에 값이 들어 있지 않은 경우 값/2에 !(값%2)를 넣어준다.
4. 실수한 점, 개선한 점
- 나중에 isTrue값을 설정해줄 때 노드의 값을 2로 나눠주게 되면 2*a-1은 a-1로 2*a는 a로 값이 달라지므로 (노드의 값+1)을 2로 나눠주어야 한다.
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#define FOR(i, k, N) for(int i=k; i<=N; i++)
using namespace std;
const int MAX_N = 10001;
const int MAX_M = 100001;
//변수의 개수 N, 절의 개수 M
int N, M;
//그래프
vector<int> Edge[MAX_M*2];
//타잔 알고리즘에서 방문한 순서, SCC로 묶인 순서, 타잔 알고리즘 탐색 순서
int visited[MAX_N*2], SCC_Count[MAX_N*2];
int visitCnt, SCC_CountTmp;
stack<int> st;
//각 Xk값 참, 거짓
bool isTrue[MAX_N];
//f(x)를 참으로 만드는 것이 가능한가?
bool isPossible;
//!x의 노드를 반환한다.
int reverse(int x)
{
return (x&1) ? (x+1):(x-1);
}
void input()
{
cin >> N >> M;
FOR(i, 1, M) {
int a, b;
cin >> a >> b;
//a가 참일 경우 2*a-1에, 거짓일 경우 2*a에 저장한다.
a = (a > 0) ? (2*a-1):(-a*2);
b = (b > 0) ? (2*b-1):(-b*2);
//그래프 생성하기
Edge[reverse(a)].push_back(b);
Edge[reverse(b)].push_back(a);
}
}
//타잔 알고리즘
int FindingSCC(int Node)
{
st.push(Node);
visited[Node] = ++visitCnt;
int parent = visited[Node];
FOR(i, 0, (int)Edge[Node].size()-1) {
int next = Edge[Node][i];
if(!visited[next])
parent = min(parent, FindingSCC(next));
else if(!SCC_Count[next])
parent = min(parent, visited[next]);
}
if(parent == visited[Node]) {
++SCC_CountTmp;
while(1) {
int here = st.top(); st.pop();
SCC_Count[here] = SCC_CountTmp;
if(here == Node)
break;
}
}
return parent;
}
void solve()
{
//아직 SCC로 묶이지 않았을 경우 SCC로 묶어주기
FOR(i, 1, N*2)
if(!SCC_Count[i])
FindingSCC(i);
isPossible = true;
for(int i=1; i<=N*2-1; i+=2)
//같은 SCC로 묶였으므로 불가능
if(SCC_Count[i] == SCC_Count[i+1])
isPossible = false;
cout << isPossible;
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
input();
//2-SAT-문제 해결하기
solve();
return 0;
}
※현재 고등학교 등교 중인 학생입니다. 이제 알고리즘을 본격적으로 공부하기 시작해서 아직 초보입니다. 혹시 제가 잘못 알고 있는 점이나 더 좋은 풀이 방법이 있어 댓글에 남겨주시면 감사히 하나하나 열심히 읽어보겠습니다. 좋아요, 단순한 댓글 한마디라도 저에겐 큰 힘이 됩니다! 감사합니다.
'알고리즘 > 백준' 카테고리의 다른 글
[C++] 2261번 가장 가까운 두 점 - 기하학, 스위핑, 분할 정복 (0) | 2022.10.24 |
---|---|
[C++] 11281번 2-SAT - 4 - 그래프 이론, 강한 연결 요소, 2-sat (0) | 2022.10.20 |
[C++] 11437번 LCA - 트리, 최소 공통 조상 (0) | 2022.10.19 |
[C++] 11438번 LCA 2 - 자료 구조, 트리, 최소 공통 조상, 희소 배열 (0) | 2022.10.19 |
[C++] 14725번 개미굴 - 자료 구조, 문자열, 트리, 트라이 (0) | 2022.10.19 |
Comments