본문 바로가기
알고리즘/백준

[백준 2637] 장난감 조립 (C++)

by fortissimo 2024. 9. 6.

https://www.acmicpc.net/problem/2637

문제


우리는 어떤 장난감을 여러 가지 부품으로 조립하여 만들려고 한다. 이 장난감을 만드는데는 기본 부품과 그 기본 부품으로 조립하여 만든 중간 부품이 사용된다. 기본 부품은 다른 부품을 사용하여 조립될 수 없는 부품이다. 중간 부품은 또 다른 중간 부품이나 기본 부품을 이용하여 만들어지는 부품이다.

예를 들어보자. 기본 부품으로서 1, 2, 3, 4가 있다. 중간 부품 5는 2개의 기본 부품 1과 2개의 기본 부품 2로 만들어진다. 그리고 중간 부품 6은 2개의 중간 부품 5, 3개의 기본 부품 3과 4개의 기본 부품 4로 만들어진다. 마지막으로 장난감 완제품 7은 2개의 중간 부품 5, 3개의 중간 부품 6과 5개의 기본 부품 4로 만들어진다. 이런 경우에 장난감 완제품 7을 만드는데 필요한 기본 부품의 개수는 1번 16개, 2번 16개, 3번 9개, 4번 17개이다.

이와 같이 어떤 장난감 완제품과 그에 필요한 부품들 사이의 관계가 주어져 있을 때 하나의 장난감 완제품을 조립하기 위하여 필요한 기본 부품의 종류별 개수를 계산하는 프로그램을 작성하시오.

 

입력


첫째 줄에는 자연수 N(3 ≤ N ≤ 100)이 주어지는데, 1부터 N-1까지는 기본 부품이나 중간 부품의 번호를 나타내고, N은 완제품의 번호를 나타낸다. 그리고 그 다음 줄에는 자연수 M(3 ≤ M ≤ 100)이 주어지고, 그 다음 M개의 줄에는 어떤 부품을 완성하는데 필요한 부품들 간의 관계가 3개의 자연수 X, Y, K로 주어진다. 이 뜻은 "중간 부품이나 완제품 X를 만드는데 중간 부품 혹은 기본 부품 Y가 K개 필요하다"는 뜻이다. 두 중간 부품이 서로를 필요로 하는 경우가 없다.

 

출력


하나의 완제품을 조립하는데 필요한 기본 부품의 수를 한 줄에 하나씩 출력하되(중간 부품은 출력하지 않음), 반드시 기본 부품의 번호가 작은 것부터 큰 순서가 되도록 한다. 각 줄에는 기본 부품의 번호와 소요 개수를 출력한다.

정답은 2,147,483,647 이하이다.

 

문제 풀이


위상 정렬 문제.

 

입력을 확인하면서 각  부품들의 indegree 값을 저장해준다.

이 문제는 완성품에 필요한 기본 부품의 개수를 구해야 하므로 N * N의 배열 usedParts를 이용해 각 부품이 몇 개 필요한 지 저장해두는 것이 필요하다. usedParts[i][j]는 부품 i를 만드는 데 필요한 부품 j의 개수를 의미한다. 기본 부품들은 다른 부품들을 필요로 하지 않고 자기 자신 하나만 필요하므로, 기본 부품의 번호가 m라고 하면 usedParts[m][m]를 1로 초기화시켜준다.

 

그 후 큐에 indegree 값이 0인 부품들을 하나씩 넣은 후, 큐가 빌 때까지 탐색한다. 초기에 넣은 부품들이 아무런 조건 없이 사용할 수 있다는 뜻이므로 기본 부품이다. 

큐의 front 값을 가져와 그 부품을 필요로 하는 중간 부품들에 대해 indegree 값을 1 감소시켜준다. 이 중간 부품은 큐의 front 부품을 K개(입력에서 주어지는 X, Y, K중 K) 필요로 하는 부품이다. 따라서 usedParts[front] 배열을 가져와 K만큼 곱해준 후, usedParts[중간 부품 번호]에 더해준다.

만약 도중 indegree값이 0이 되면 이제 그 부품은 조건 없이 사용할 수 있다는 뜻이 되므로 큐에 넣어준다.

 

각 기본 부품에 대해 usedParts[N] 값을 출력해주면 정답이다.

 

아래는 코드.

더보기
#include <iostream>
#include <queue>
#include <vector>
#include <utility>
using namespace std;

int main()
{
	cin.tie(NULL);
	ios::sync_with_stdio(false);

	int N, M, X, Y, K;
	queue<int> q;
	vector<int> basic;
	cin >> N;
	cin >> M;
	int* inDegree = new int[N + 1];
	vector<pair<int, int>>* next = new vector<pair<int, int>>[N + 1];
	int** usedParts = new int* [N + 1];
	for (int i = 0; i < N + 1; i++)
	{
		inDegree[i] = 0;
		usedParts[i] = new int[N + 1];
		for (int j = 0; j < N + 1; j++)
		{
			usedParts[i][j] = 0;
		}
	}
	for (int i = 0; i < M; i++)
	{
		cin >> X >> Y >> K;
		next[Y].push_back(pair(X, K));
		inDegree[X]++;
	}
	for (int i = 1; i <= N; i++)
	{
		if (inDegree[i] == 0)
		{
			q.push(i);
			basic.push_back(i);
			usedParts[i][i] = 1;
		}
	}
	while (!q.empty())
	{
		int current = q.front();
		int* currentUsedParts = usedParts[current];
		for (int i = 0; i < next[current].size(); i++)
		{
			int nextPart = next[current].at(i).first;
			int needCount = next[current].at(i).second;
			for (int j = 0; j < N + 1; j++)
			{
				usedParts[nextPart][j] += currentUsedParts[j] * needCount;
			}
			inDegree[nextPart]--;
			if (inDegree[nextPart] == 0)
			{
				q.push(nextPart);
			}
		}
		q.pop();
	}
	for (int i = 0; i < basic.size(); i++)
	{
		cout<<basic.at(i)<<" " << usedParts[N][basic.at(i)] << "\n";
	}
	return 0;
}