알고리즘 동아리 8주차 골드 문제 풀이

3 분 소요

A번 - 사회망 서비스(SNS) (2533)

문제 설명

트리가 주어진다. 어떤 노드의 주위가 모두 얼리어답터이면 그 노드는 새로운 아이디어를 받아들인다. 얼리어답터를 최소로 해서 모든 노드가 새로운 아이디어를 받아들이게 하는 경우를 구하는 문제이다.

풀이

  • 점화식

    dp[i][j]: 부모가 i인 서브트리의 얼리 어답터의 최대값
    (j가 0이면 i 노드가 얼리어답터, j가 1이면 i 노드가 얼리어답터X)

  • 수도 코드

dp[i][0] = dp[i->child1][1] + dp[i->child2][1] + ...
dp[i][1] = min(dp[i->child1][1], dp[i->child1][0])
    + min(dp[i->child2][1], dp[i->child2][0])
    + ...

코드

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;

vector<int> adj[1000001];
vector<int> children[1000001];
bool visited[1000001];

// j가 0이면 i번째 노드가 얼리어답터가 아닌 서브 트리의 얼리어답터 최소 수
// j가 1이면 i번째 노드가 얼리어답터인 서브 트리의 얼리어답터 최소 수
int dp[1000001][2];

int n;
int a, b;

void makeChildren(int root);
int solve(int i, int j);

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

	memset(dp, -1, sizeof(dp));

	cin >> n;
	for (int i = 0; i < n - 1; i++)
	{
		cin >> a >> b;
		adj[a].push_back(b);
		adj[b].push_back(a);
	}

	visited[1] = true;
	makeChildren(1);

	dp[1][0] = solve(1, 0);
	dp[1][1] = solve(1, 1);

	cout << min(dp[1][0], dp[1][1]);
	return 0;
}

void makeChildren(int root)
{
	vector<int> rootChildren;

	for (int adjNode : adj[root])
	{
		if (visited[adjNode])
			continue;

		children[root].push_back(adjNode);
		visited[adjNode] = true;
		rootChildren.push_back(adjNode);
	}

	for (int child : rootChildren)
	{
		makeChildren(child);
	}
}

int solve(int i, int j)
{
	int& ret = dp[i][j];
	if (ret != -1)
		return ret;

	if (j == 0)
		ret = 0;
	else if (j == 1)
		ret = 1;

	// relChildren: relevant children (관련된 자식들)
	vector<int> relChildren = children[i];

	if (j == 0)
	{
		for (int child : relChildren)
		{
			ret += solve(child, 1);
		}
	}
	else if (j == 1)
	{
		for (int child : relChildren)
		{
			ret += min(solve(child, 0), solve(child, 1));
		}
	}

	return ret;
}

소감

매우 interesting한 문제이다. DP 꿀잼 ㅋㅋㅋ 트리 DP 문제는 그래프를 트리로 바꾸는 테크닉이 필요한데 코드의 makeChildren 함수를 잘 기억하자.


B번 - 전생했더니 슬라임 연구자였던 건에 대하여 (Hard) (14698)

문제 설명

슬라임이 여러 마리 있다. 슬라임을 적절히 합성하여 1마리의 슬라임으로 만드려 한다. 두 슬라임 에너지의 곱의 크기만큼 전기 에너지가 소요되고 그 크기가 새로운 슬라임의 에너지가 된다. 합성 단계마다 필요한 전기 에너지를 모두 곱한 값이 최소가 되도록 합성을 해야 한다. 슬라임을 끝까지 합성했을 때 청구될 비용의 최소값을 1,000,000,007로 나눈 나머지를 출력해야 한다.

풀이

작은것부터 곱해 나가면 된다. moduler 연산이 매우매우 중요한데 작은거 2개를 곱한 뒤 정답 = 정답 * (곱한 값 % DIV) % DIV 으로 계산해야 정확하게 된다. 안 그러면 Overflow가 날 가능성이 크다.

코드

#include <iostream>
#include <queue>
#include <functional>

#define DIV 1000000007

using namespace std;

typedef long long ll;

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

	int t;
	cin >> t;
	while (t--)
	{
		priority_queue<ll, vector<ll>, greater<ll>> pq;

		int n;
		cin >> n;

		ll d;
		while (n--)
		{
			cin >> d;
			pq.push(d);
		}

		ll answer = 1;
		while (pq.size() != 1)
		{
			ll p1 = pq.top();
			pq.pop();
			ll p2 = pq.top();
			pq.pop();
			ll newEle = p1 * p2;
			answer = answer * (newEle % DIV) % DIV;
			pq.push(newEle);
		}

		cout << answer << "\n";
	}
}

소감

작은것부터 곱해나가면 된다는것을 빠르게 catch 하지 못하였다. 사실 이 문제는 실버급 문제라고 생각한다. 우선순위 큐만 쓰면 되기 때문이다.


C번 - 소형기관차 (2616)

문제 설명

기관차가 끌고 가던 객차에 타는 승객을 소형 기관차 3대로 나눠서 끌고 갔을때 최대로 실을 수 있는 승객의 수를 구하는 문제이다.

풀이

소형 기관차가 끌 수 있는 객차의 수를 n이라 하자.

  • 점화식

    dp[i][j]: i번째 객차까지 세었을 때 j개의 소형 기관차가 이미 결정된 경우 실을 수 있는 승객의 최대 값
    prev = dp[i - n][j - 1]
    boundSum(start, end): 객차의 start부터 end까지의 합
    dp[i][j] = max(dp[i - 1][j], prev + boundSum(i - n + 1, i))

코드

#include <iostream>
#include <algorithm>
using namespace std;

int s; // 기관차 사이즈
int arr[50001];
int n; // 최대로 끌 수 있는 객차 수
int dp[50001][4];

int boundSum(int start, int end)
{
	int ret = 0;

	for (int i = start; i <= end; i++)
		ret += arr[i];

	return ret;
}

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

	cin >> s;
	for (int i = 1; i <= s; i++)
	{
		cin >> arr[i];
	}
	cin >> n;

	int answer = -1;
	for (int i = 1; i <= s; i++)
	{
		for (int j = 1; j <= 3; j++)
		{
			int prev;
			if (i - n >= 1)
				prev = dp[i - n][j - 1];
			else
				prev = 0;

			dp[i][j] = max(dp[i - 1][j], prev + boundSum(i - n + 1, i));

			if (j == 3)
				answer = max(answer, dp[i][3]);
		}
	}

	cout << answer;
}

소감

골드 DP 문제가 고민을 조금만 하니 풀려서 신기했다. 슬슬 실력 오른듯? ㅋㅋ