Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 |
Tags
- 백준 #다익스트라 #dijkstra #9370 #c++
- boj #백준
- BOJ
- backtracking #codetree #디버깅 #삼성코테
- 3343
- 호반우 상인
- hcpc
- c++ #입출력 #속도 #ios #sync_with_stdio #cin #cout #tie
- 22869
- graph #최단경로
- 30870
- 코딩
- 이분탐색 #dp #11053
- 20117
- 백준
- graph
- C++
- 3D #Reconstruction #computer #vision #volume #metric #tsdf #kinect #fusion
- 16202
- 최소 #공배수 #최대 #공약수 #유클리드 #호제법 #lcm #gcd #c++ #boj #3343 #백준 #장미
- 1174
- 레드아보
- 진법변환 #2to10 #10to2 #이진법 #십진법 #변환 #bitset #c++
- 줄어드는수
- 투포인터 #백준 #boj #20922 #22862
- 사이클 없는 그래프
- N번째큰수
- c++ #boj #
- LIS #가장긴증가하는부분수열 #
- 쌤쌤쌤
Archives
- Today
- Total
hyunjin
[BOJ 30870 사이클 없는 그래프 c++ 풀이] 본문
728x90
문제
주어진 그래프에서 t 단계 별로 인접한 노드와 연결 엣지 지워나감.
최초로 사이클 생성되지 않는 t 구하기
N,M <= 200,000
풀이 전략
사이클이 사라지는 것을 찾긴 어렵다. → 사이클 생성 여부 확인으로 바꾸면 쉽다.
주어진 순서대로 간선을 지우고 사이클 생성 여부를 확인 , 지울때마다 사이클을 판단 → TLE
그렇다면 역순으로 판단하자.
마지막에 사라졌던 간선들부터 시작하여 처음 사라진 간선들 순서로 그래프가 복원하자.
간선이 지워지는 역순으로 그래프를 생성해나가 보면, 어느 순간 사이클 발생
→ (전체 T - 사이클 생성 되는 t) 가 답
요약
- 간선이 언제 지워지는지는 알겠음 : BFS를 적절히 응용
- 지워지는 역순으로 간선을 연결해나가가다가 사이클이 생기는 시점 판단
- 인풋으로 주어지는 K개 시작 노드를 기준으로 t 단계에 지워지는 간선 저장.
- => BFS, Stack 이용
- 사이클 판단은 union-find로 판단 : merge가 안되는 경우 = 이미 같은 부모 = 사이클
- 사이클 생성 여부 disjoint set
- Union Find
3가지 오류
문제 풀며 틀렸던 부분을 정리해봤다.
- 사이클 제거를 순차적으로 접근
- 초기 K개 주어진 노드를 시작으로 해당 노드와 연결된 relation을 지운 상태에서 사이클 형성 여부 검사하고
- 다시 연결된 노드를 지우고 검사하고
- 이 방법 자체로 시간 초과다. 순차접근이 아닌 역으로 접근하는게 이 문제의 핵심이다.
- 사이클 제거 시 relation 전체를 반복적으로 접근했다.
- 예로, 1번 노드 제거하고 1번 노드와 관련된 노드만 보는 것이 아닌 전체 relation을 보며 1번 관련된거면 pass하고 이런 식으로 전체 관계를 반복적으로 매 스텝마다 봤다.
- relation 200,000개 → 이걸로 제거한 사이클 찾거나 새로 만들고 반복적으로 하면 TLE, 메모리 초과
- 중복 간선 주어진 경우
- 인풋에 주어지는 중복 간선을 제거하고 시작하라는 의미라고 해석했지만, 중복 간선 또한 사이클 발생하는 요소로 생각해야한다.
틀린 코드 → 2번 오류 케이스
#include<iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <stack>
//#include <tuple>
#define MAX_N 200001
using namespace std;
typedef pair<int, int> pii;
int N, M;
vector<pii> relation;
vector<int> toDel; // 지울 노드
stack<vector<pii>> toAdd; // 역순
bool erased[MAX_N + 1] = { 0, };
int par[MAX_N + 1];
int Find(int a) {
if (a == par[a]) return a;
return par[a] = Find(par[a]);
}
bool Union(int a, int b) {
a = Find(a);
b = Find(b);
if (a == b) return false;
par[b] = a;
return true;
}
bool hasCycle(vector<pii>& vec) {
for (const pii& p : vec) {
if (!Union(p.first, p.second))
return true; // 사이클 형성 관계
}
return false;// 사이클 안생긴다.
}
void bfs() {
// 지워야할 엣지 사전에 구하기
while (M) {
vector<pii> add_rel; // stack에 추가될
vector<pii> new_rel; // 아직 추가되지 않은
for (int e : toDel) erased[e] = true;
toDel.clear();
for (const pii& p : relation) {
int u = p.first, v = p.second;
// 둘다 사용 안됨
if (!erased[u] && !erased[v]) {
new_rel.push_back({ u,v });
continue;
}
// 둘 중 하나라도 사용됨. 지울 대상
add_rel.push_back({ u,v });
if (!erased[u])toDel.emplace_back(u);
if (!erased[v])toDel.emplace_back(v);
}
M -= add_rel.size();
toAdd.push(move(add_rel));
relation = move(new_rel);
}
}
int solve() {
bfs(); //관계 역순 형성
int ret = toAdd.size();
for (int i = 1; i <= N; i++) par[i] = i;
while (!toAdd.empty()) {
ret--;
if (hasCycle(toAdd.top()))
break;
toAdd.pop();
}
return ret + 1;
}
void input() {
int K;
cin >> N >> M >> K;
// 중복 없는 간선 위해
set<pii> s;
for (int i = 0; i < M; i++) {
int a, b;
cin >> a >> b;
if (b < a) swap(a, b);
s.insert({ a,b });
}
relation.assign(s.begin(), s.end());
while(K--) {
int a;
cin >> a;
toDel.emplace_back(a);
}
}
int main(int argc, char** argv)
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
input();
cout << solve();
return 0;
}
최종 코드
#include<iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
#define MAX_N 200001
using namespace std;
typedef pair<int, int> pii;
int N, M;
vector<int> relation[MAX_N+1];
stack<vector<pii>> toAdd; // 역순
queue<int> toDel;
bool erased[MAX_N + 1] = { 0, };
int par[MAX_N + 1];
int Find(int a) {
if (a == par[a]) return a;
return par[a] = Find(par[a]);
}
bool Union(int a, int b) {
a = Find(a);
b = Find(b);
if (a == b) return false;
par[b] = a;
return true;
}
bool hasCycle(vector<pii>& vec) {
for (const pii& p : vec) {
if (!Union(p.first, p.second))
return true; // 사이클 형성 관계
}
return false;// 사이클 안생긴다.
}
void bfs(int n) {
while (n) {
int cnt = toDel.size();
toAdd.push(vector<pii>() );
vector<pii> &vec = toAdd.top();
while (cnt--) {
int curr = toDel.front();
toDel.pop();
if (erased[curr]) continue;
erased[curr] = true;
n--;
for (int nex : relation[curr]) {
if (erased[nex]) continue;
vec.push_back({ curr,nex });
toDel.push(nex);
}
}
}
}
int solve() {
bfs(N); //관계 역순 형성
while (toAdd.top().empty())toAdd.pop();
int ret = toAdd.size();
for (int i = 1; i <= N; i++)
par[i] = i;
while (!toAdd.empty()) {
ret--;
if (hasCycle(toAdd.top()))
break;
toAdd.pop();
}
return ret + 1;
}
void input() {
int K;
cin >> N >> M >> K;
for (int i = 0; i < M; i++) {
int a, b;
cin >> a >> b;
relation[a].push_back(b);
relation[b].push_back(a);
}
while(K--) {
int a;
cin >> a;
toDel.push(a);
}
}
int main(int argc, char** argv)
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
input();
cout << solve();
return 0;
}
728x90
'알고리즘 연습 > 백준' 카테고리의 다른 글
[BOJ 20440] imso, 누적 합, 좌표 압축 (0) | 2024.06.30 |
---|---|
[BOJ22869 징검다리 건너기(small)] 다이나믹 프로그래밍 dp (0) | 2024.05.18 |
[BOJ 5875 오타 c++] 자료구조 누적 합 (0) | 2024.04.28 |
[BOJ 3343 장미] 정수론, LCM (0) | 2024.04.25 |
[BOJ 2933 미네랄] 구현 그래프 너비 깊이 우선 (0) | 2024.04.23 |