일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- 이분 탐색
- 미래는_현재와_과거로
- 가끔은 말로
- 자바스크립트
- 크루스칼
- DP
- 분할 정복
- Overfitting
- BFS
- 회고록
- pytorch
- 우선 순위 큐
- object detection
- tensorflow
- NEXT
- 문자열
- dfs
- 플로이드 와샬
- 백트래킹
- 너비 우선 탐색
- 가끔은_말로
- c++
- 다익스트라
- 2023
- 조합론
- lazy propagation
- dropout
- 세그먼트 트리
- 알고리즘
- back propagation
Archives
- Today
- Total
Doby's Lab
백준 2416번: 문 (C++) 본문
https://www.acmicpc.net/problem/2416
2416번: 문
첫째 줄에 수로의 개수 N (1 ≤ N ≤ 250,000)과 스위치의 개수 M (1 ≤ M ≤ 500,000)이 주어진다. 둘째 줄부터 N개의 줄에 각 수로의 정보가 주어진다. 수로의 정보는 4개의 숫자로 이루어져 있고, a, sa, b
www.acmicpc.net
Solved By: 2-SAT, SCC
메모리 초과가 계속 나서 최대한의 최적화를 해보려 했지만 원인이 무엇인지 알 수 없었습니다.
그러다가 바보같이 SCC를 구하여 담아두는 2차원 vector배열이 필요 없다는 것을 알아내어 맞출 수 있었습니다.
2-SAT은 결국 각 변수가 몇 번째 SCC에 속하는지만 알고 있으면 된다는 걸 깨닫습니다. (물론 이 문제에 한정해서입니다.)
문제 해석:
각 수로에는 문의 열고, 닫고를 책임지는 스위치가 2개 있습니다. 수로에 물이 통하지 않기 위해서는 적어도 하나의 수로만 닫혀있으면 됩니다.
즉, 한 수로를 Clause하나라고 볼 수 있고, 각각의 문을 책임지는 스위치가 변수라고 할 수 있습니다.
(ex: 2번 스위치라면 2번 문이 있는 수로와 연관이 있습니다.)
이대로 2-SAT을 하여 답을 구할 수 있겠다고 생각하지만, 이 문제에서 F -> F == true라고 처리해버린다면 문이 다 오픈된 상태이기 때문에 무조건 모든 명제를 T -> T == true로 해야 합니다. (T -> F == false라서 안 됨.)
#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
#define MAX 1000002
#define pii pair<int, int>
using namespace std;
int n, m;
vector<int> adj[MAX];
stack<int> s;
vector<int> visitedOrder(MAX, -1);
vector<int> sccNum(MAX, -1);
vector<pii> satOrder;
int order = 0, sccCnt = 0;
int dfs(int now){
int minOrder = visitedOrder[now] = ++order;
s.push(now);
for(int i = 0; i < adj[now].size(); i++){
int next = adj[now][i];
if(visitedOrder[next] == -1){
minOrder = min(minOrder, dfs(next));
}
else if(sccNum[next] == -1){
minOrder = min(minOrder, visitedOrder[next]);
}
}
if(visitedOrder[now] == minOrder){
while(true){
int temp = s.top(); s.pop();
sccNum[temp] = sccCnt;
satOrder.push_back({temp, sccNum[temp]});
if(temp == now) break;
}
sccCnt++;
}
return minOrder;
}
void tarjan(){
for(int i = 2; i <= 2 * m + 1; i++){
if(visitedOrder[i] == -1) dfs(i);
}
}
bool cmp(pii a, pii b){
return a.second > b.second;
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> m;
for(int i = 0; i < n; i++){
int a, sa, b, sb;
cin >> a >> sa >> b >> sb;
int not_a, not_b;
if(sa == 0){ // 스위치가 닫혀야 문이 닫힌다.
a *= 2;
not_a = a + 1;
}
else{ // 스위치를 켜야 문이 열린다
a = (a * 2) + 1;
not_a = a - 1;
}
if(sb == 0){
b *= 2;
not_b = b + 1;
}
else{
b = (b * 2) + 1;
not_b = b - 1;
}
adj[not_a].push_back(b);
adj[not_b].push_back(a);
}
tarjan();
for(int i = 2; i <= 2 * m; i += 2){
if(sccNum[i] == sccNum[i + 1]){
cout << "IMPOSSIBLE";
return 0;
}
}
sort(satOrder.begin(), satOrder.end(), cmp);
vector<int> answer(m + 1, -1);
for(auto p : satOrder){
int vertex = p.first;
int varIdx = vertex / 2;
int isTrue = (vertex % 2 == 0);
if(answer[varIdx] != -1) continue;
// 이 문제에서는 false -> false는 문이 모두 열려있는 상태다.
// 즉, true -> true가는 방법을 택해야 한다.
answer[varIdx] = isTrue;
}
for(int i = 1; i < answer.size(); i++){
cout << answer[i] << '\n';
}
return 0;
}
728x90
'PS > BOJ' 카테고리의 다른 글
백준 11438번: LCA 2 (C++) (0) | 2022.05.01 |
---|---|
백준 4305번: 성격 진단 테스트 (C++) (0) | 2022.04.30 |
백준 1967번: 트리의 지름 (C++) (0) | 2022.04.30 |
백준 9237번: 이장님 초대 (C++) (0) | 2022.04.29 |
백준 7420번: 맹독 방벽 (C++) (0) | 2022.04.28 |