※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※
이번에 볼 문제는 백준 8310번 문제인 Riddle이다.
문제는 아래 링크를 확인하자.
https://www.acmicpc.net/problem/8310
8310번: Riddle
In the first line of the standard input there are three integers: n (1 ≤ n ≤ 1,000,000), denoting the number of towns in Voldebyte's riddle, m (0 ≤ m ≤ 1,000,000), denoting the number of roads, and k (1 ≤ k ≤ n), denoting the number of count
www.acmicpc.net
각 i번째 town이 capital이면 true, 그렇지 않으면 false 값을 갖는 bool 변수
우선 "각 간선의 적어도 한쪽 끝은 capital이어야 한다" 조건은 두 town의 번호가
"각 country에는 capital이 정확히 하나씩 있어야한다"는 조건은 "각 country에는 capital이 최대 하나 존재한다" 및 "각 country에는 capital이 최소 하나 존재한다"는 두 조건으로 나누어 생각할 수 있다. 한편, 후자 조건 없이 전자 제약만을 걸어 답을 얻은 다음 capital이 정해지지 않은 국가의 경우 아무 도시나 capital로 지정한다면 후자 조건이 같이 있는 경우의 답을 얻을 수 있고 이는 어렵지 않으므로 후자 조건 없이 전자 조건만을 이용한 해를 찾는 것으로 문제를 해결할 수 있게 된다.
이제 "각 country에는 capital이 최대 하나 존재한다" 조건을 CNF로 표현해보자. 어떤 country에 속한 town이
새로운 bool 변수
위의 각 논리식들은 2-CNF로 나타낼 수 있으므로 2-SAT을 해결하는 선형 알고리즘을 이용해 이 문제를 해결할 수 있다. 시간복잡도는
그래프의 크기 및 입출력의 양이 많아 같은 알고리즘이더라도 속도가 느린 언어 또는 비효율적인 구현으로는 통과하기 어려운 듯하다. 이는 그래프를 모델링할 때 각 country에 capital이 최소 하나 존재 존재한다는 조건을 제외하고 별도의 처리로 조건을 만족시킨 이유이기도 하다. 혹시 그래도 시간 초과가 날 경우 불필요한 구현이 없는지 확인해 문제를 해결하자.
아래는 제출한 소스코드이다.
#define NODE 4000001
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int N, M, K; const int gap = 2000000; // 2-SAT 정점개수(TF분할 말고), T와F노드 사이 값의 차이
vector<int> G[NODE];
int dfi[NODE]; int dfidx = 1;
int sci[NODE]; int scidx = 1;
vector<int> stk;
int twosatdfs(int current) {
stk.emplace_back(current);
int ret = dfi[current] = dfidx++;
for (auto node : G[current]) {
if (sci[node]) continue;
if (dfi[node]) ret = min(ret, dfi[node]);
else ret = min(ret, twosatdfs(node));
}
if (ret == dfi[current]) {
while (stk.back() != current) {
sci[stk.back()] = scidx;
stk.pop_back();
}
sci[stk.back()] = scidx++;
stk.pop_back();
}
return ret;
}
bool ans[NODE];
bool TWOSAT() {
for (int i = 1; i <= N; i++) {
if (sci[i]) continue;
twosatdfs(i);
}
for (int i = 1 + gap; i <= N + gap; i++) {
if (sci[i]) continue;
twosatdfs(i);
}
for (int i = 1; i <= N; i++) {
if (sci[i] < sci[i + gap]) ans[i] = 1;
else if (sci[i] > sci[i + gap]) ans[i] = 0;
else return 0;
}
return 1;
}
int nxtid;
vector<int> towns[1000001];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> N >> M >> K;
while (M--) {
int x, y; cin >> x >> y;
G[x + gap].emplace_back(y);
G[y + gap].emplace_back(x);
}
nxtid = N;
for (int k = 1; k <= K; k++) {
int T; cin >> T;
nxtid++;
int old; cin >> old;
G[old].emplace_back(nxtid);
G[nxtid + gap].emplace_back(old + gap);
towns[k].emplace_back(old);
for (int t = 1; t < T; t++) {
nxtid++;
int x; cin >> x;
G[x].emplace_back(nxtid);
G[nxtid + gap].emplace_back(x + gap);
G[nxtid - 1].emplace_back(nxtid);
G[nxtid + gap].emplace_back(nxtid - 1 + gap);
G[nxtid - 1].emplace_back(x + gap);
G[x].emplace_back(nxtid - 1 + gap);
towns[k].emplace_back(x);
old = x;
}
}
N = nxtid;
if (TWOSAT()) {
cout << "TAK\n";
for (int k = 1; k <= K; k++) {
bool has_capital = 0;
for (auto t : towns[k]) {
if (ans[t]) has_capital = 1, cout << t << ' ';
}
if (!has_capital) cout << towns[k].back() << ' ';
}
}
else cout << "NIE";
}
'BOJ' 카테고리의 다른 글
[BOJ 4138 // C++] Paper Route (1) | 2023.10.17 |
---|---|
[BOJ 4139 // C++] Octagons (0) | 2023.10.16 |
[BOJ 18259 // C++] Greedy Pie Eaters (0) | 2023.10.14 |
[BOJ 18260 // C++] Bessie's Snow Cow (0) | 2023.10.13 |
[BOJ 11999 // C++] Milk Pails (Bronze) (1) | 2023.10.12 |