백준 14942번 개미
문제
개미집은 n개의 방으로 구성되어 있으며 n개의 방은 1번부터 n번 까지 번호가 부여되어 있다. 그 중에서 1번 방은 지면에 바로 연결되어 있는 방이다. 각 방들은 서로 굴을 통해 연결되어 있다. 각 굴을 이동하기 위해서는 굴의 길이만큼 에너지가 소모된다.
개미는 집짓기의 달인이기 때문에 불필요한 굴은 짓지 않는다. 그래서 굴을 타고 한 방에서 다른 방으로 갈 수 있는 경로는 항상 존재하며 유일하다. 임의의 두 방 사이의 거리는 두 개의 방을 연결하는 경로를 구성하는 굴의 길이의 합이다.
겨울잠을 자던 개미들은 겨울잠에서 깨어나 지면으로 올라가 햇살을 보고 싶어한다. 그렇기 때문에 지면과 연결된 1번 방으로 이동을 하려고 한다. 하지만 불행하게도 개미는 긴 겨울잠을 자느라 축적해 놓은 에너지가 적다. 그래서 개미는 에너지를 1번 방에 도달하기 전에 모두 소모 할 수도 있다. 이렇게 에너지가 0이 된 개미는 더 이상 움직일 수 없다. 또한 1번 방에 도착한 개미는 더 이상 움직이지 않는다.
현재 모든 방에는 개미가 한 마리씩 있고 각각의 개미는 각자 축적된 에너지를 가지고 있다. 잠에서 깨어난 모든 개미는 1번 방을 향해서 이동한다. 이때 각각의 개미에 대해 도달할 수 있는 방 중에서 가장 1번 방에 가까운 방의 번호를 출력하시오.
문제풀이
사용한 알고리즘: DFS, 희소테이블(1) 생각
거리를 2진수로 쪼개 생각합니다. (희소테이블)예를들어 10 = 2^3 + 2^1 입니다.
개미방은 최대 10^5 < 2^17 입니다.
(2) 코드 11~12
'parent[i][k] : pair < i의 2^k번째 조상, 가는 비용 >' 으로 설정했습니다.부모값 parent[i][0] = pair< i의 부모, 가는 비용 > 입니다.
(3) 코드 16~25
1번 방부터 DFS 돌면서 부모값 parent[i][0] 을 설정해줍니다.(4) 코드 42~50
부모값 parent[i][0] 을 갖고 나머지 조상들에 대한 값들도 구해줍니다.(4) 코드 52~66
구해놓은 조상들과 그에 도달하는 에너지 를 갖고 각각의 방에서 도달 가능한 방을 구해줍니다.예를들어 21 = 2^4 + 2^2 + 2^0 입니다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <bits/stdc++.h> | |
using namespace std; | |
const int MAX = 1e5; | |
// 2^17 > 1e5 | |
const int DMAX = 17; | |
typedef pair<int, int> pii; | |
int N; | |
int Energy[MAX+5]; | |
vector<pii> adj[MAX+5]; | |
// parent[i][k] : < i의 2^k 조상, 가는비용 > | |
pii parent[MAX+5][DMAX+1]; | |
// 도달 가능한 방 | |
int ans[MAX+5]; | |
void Path(int curr){ | |
for(auto it: adj[curr]){ | |
int child = it.first; | |
int w = it.second; | |
// 중복 체크 | |
if(parent[child][0].first!=-1 || child==1) continue; | |
parent[child][0] = pii(curr,w); | |
Path(child); | |
} | |
} | |
int main(){ios_base::sync_with_stdio(false); cout.tie(NULL); cin.tie(NULL); | |
cin >> N; | |
for (int i = 1; i <= N; ++i){ | |
cin >> Energy[i]; | |
// 초기화 | |
for (int k = 0; k <= DMAX; ++k) | |
parent[i][k] = pii(-1,-1); | |
} | |
for (int i = 0; i < N-1; ++i){ | |
int a, b, w; | |
cin >> a >> b >> w; | |
adj[a].push_back(pii(b,w)); | |
adj[b].push_back(pii(a,w)); | |
} | |
// 1번방, 깊이는 0 | |
Path(1); | |
// parent 만들기 | |
for (int k = 0; k < DMAX; ++k){ | |
for (int i = 2; i <= N; ++i){ | |
int Father = parent[i][k].first; | |
int w = parent[i][k].second; | |
if(Father!=-1) | |
parent[i][k+1] = pii(parent[Father][k].first, w+parent[Father][k].second); | |
} | |
} | |
for (int i = 1; i <= N; ++i){ | |
int Go = DMAX; | |
int E = Energy[i]; // i가 갖고 있는 에너지 | |
int temp = i; // i 가 도달 가능한 최대 방 | |
while(Go>=0 && temp!=1 && E>0){ | |
if(parent[temp][Go].first!=-1 && E >= parent[temp][Go].second){ | |
// 순서 주의! Energy 바꾸고 temp 움직여야함!! | |
E -= parent[temp][Go].second; | |
temp = parent[temp][Go].first; | |
} | |
Go--; | |
} | |
ans[i] = temp; | |
} | |
for (int i = 1; i <= N; ++i) | |
cout << ans[i] << '\n'; | |
return 0; | |
} |
댓글
댓글 쓰기
긴 글 읽어주셔서 감사합니다.
궁금한게 있으시다면 댓글 달아주세요!