이 문제는 언뜻봐서는 그래프와 관련없어 보인다.
하지만 장난감의 수를 한자리씩 조합해 나가는 과정에서 그래프의 최단경로 알고리즘을 활용할 수 있다.
우선 문제에서 요구한 조건은 다음과 같다.
1. n+m 이상이어야 한다. (n명의 아이들에게 하나씩 나누어주고, m명의 아이들에게 하나씩 더 나누어주는 경우)
2. n으로 나눈 나머지가 m이어야 한다.
( n명의 아이들에게 k개씩 나누어주고, m명의 아이들에게 하나씩 더 나누어주기 때문에 n*k+m의 형태가 된다)
3. d에 포함된 숫자들로만 이루어져 있어야 한다.
앞에서 웨브바짐 문제에서 활용하였던 나머지 연산의 분배법칙을 다시 활용할 수 있다.
3번 조건을 만족하는 장난감의 수를 한자리씩 만들어갈때, 지금까지 만든 수를 c라고 하면
c를 n으로 나눈 나머지를 알면, c의 뒤에 숫자 x를 붙인 결과값을 n으로 나눈 나머지도 구할 수 있다.
((c mod n) * 10 + x) mod n = (c * 10 +x) mod n 이기 때문이다.
즉, c를 n으로 나눈 나머지에 10을 곱해 x를 더한 값을 n으로 나눈 나머지를 구하면 된다.
그러면 문제를 해결하기 위해 장난감의 개수에 대해 d에 포함된 숫자들 중 하나씩 조합해가면서 n으로 나눈 나머지를 계산하면된다.
그리고 n+m 이상인 최소 장난감의 개수를 구하면 된다.
문제를 해결하기 위해 c를 n으로 나눈 나머지를 정점으로 갖는 그래프를 만들도록 한다.
따라서 그래프는 0 부터 n-1 까지의 정점을 가지고 있다.
각 간선들은 현재 c에 숫자 x를 붙였을 때 n으로 나눈 나머지값의 정점으로 향하도록 하고, x로 번호를 매긴다.
즉 각 정점으로 향하는 경로의 간선 번호를 조합하면, 해당 c의 값을 구할 수있다.
나머지가 m인 장난감의 최소 개수를 구해야 하므로, 정점 m으로 가는 최단 경로를 구해야 한다.
최단 경로일 때 나머지가 m인 장난감의 개수의 자릿수가 최소가 되기 때문이다.
단, 이때 한 정점에서 다른 정점으로 갈 때 간선이 여러개 있다면 간선의 번호가 작은 순으로 방문하도록 한다.
장난감의 개수의 자릿수가 같다면, 자릿수가 높은 자리부터 숫자가 최소여야 하기 때문이다.
그러면 이제 n+m 이상이어야 한다는 조건만 만족하면 된다.
현재 그래프에서는 각 정점에 도달했을 때 만들어진 c의 값이 n 이하 일수도 있기 때문에, n+m 이상이어야 한다는 조건을 만족하지 못한다.
이 문제를 해결하기 위해서 각 나머지 값들에 대해 정점을 두개씩 만들도록 한다.
하나는 현재까지 만든 c값이 n 미만일 경우 도달하는 정점이고, 하나는 n이상일때 도달하는 정점이다.
전자를 흰색 정점, 후자를 회색 정점이라고 하면, 우리가 찾는 경로는 흰색 0 정점에서 회색 m 정점으로 향하는 최단 경로인 것이다.
정점을 구분하기 위해 흰색 정점은 0부터 n-1 까지, 회색 정점은 n 부터 2n-1 까지로 번호 매긴다.
// 어린이날 문제를 해결하는 최단 경로 알고리즘
// 현재 here 정점에서 edge 간선을 따라갔을 때 나오는 정점의 번호
int append(int here, int edge, int n){
int there = here * 10 + edge;
if( there < n) return there;
else return there % n + n;
}
string gift(string digits, int n, int m){
sort(digits.begin(), digits.end()); // 간선의 번호를 미리 오름차순으로 정렬하면 작은 순서부터 방문할 수 있다.
vector<int> parents(2 * n, -1); // bfs 스패닝 트리에서 각 정점의 부모
vector<int> choice(2 * n, -1); // 각 정점과 정점의 부모가 연결된 간선의 번호
queue<int> q;
parents[0] = 0;
q.push(0);
while(!q.empty()){
int here = q.front();
q.pop();
for(int i = 0; i < digits.size(); i++){
int there = append(here, digits[i] - '0', n);
if(parents[there] == -1){
parents[there] = here;
choice[there] = digits[i] - '0';
q.push(there);
}
}
}
if(parents[n + m] == -1) return "IMPOSSIBLE"; // 회색 m 정점에 도달하지 못했으면 실패
// 회색 m 정점에서 bfs 스패닝 트리의 부모를 따라가며 흰색 0 정점까지 간선의 번호를 기록한다.
string ret;
int here = n + m;
while(parents[here] != here){
ret += char(choice[here] + '0');
here = parents[here];
}
reverse(ret.begin(), ret.end()); // 결과를 뒤집는다.
return ret;
}
'DEVELOP > 알고리즘' 카테고리의 다른 글
6. BFS - 하노이의 탑(hanoi4) 문제 (0) | 2019.03.11 |
---|---|
6. BFS - 최단 경로 전략 (0) | 2019.03.11 |
6. BFS - Sorting Game(sortgame) 문제 (0) | 2019.03.07 |
6. BFS (0) | 2019.02.28 |
5. DFS - 회의실 배정(meetingroom) 문제 (0) | 2019.02.26 |