득이공간

[백준 C++] 20303 할로윈의 양아치 - 다이나믹프로그래밍 본문

PS/알고리즘 문제풀이

[백준 C++] 20303 할로윈의 양아치 - 다이나믹프로그래밍

쟁득 2024. 3. 21. 16:12

 

 

20303번: 할로윈의 양아치

첫째 줄에 정수 $N$, $M$, $K$가 주어진다. $N$은 거리에 있는 아이들의 수, $M$은 아이들의 친구 관계 수, $K$는 울음소리가 공명하기 위한 최소 아이의 수이다. ($1 \leq N \leq 30\ 000$, $0 \leq M \leq 100\ 000$,

www.acmicpc.net

#include <iostream>
using namespace std;

typedef pair<int, int> team;

int N, M, K;
int Candy[30000];
int Root[30000];
bool Visited[30000];

int TeamNum;
team Team[30000];
int DP[30001][3001];

int Find(int Node)
{
	if (Node == Root[Node])
	{
		return Node;
	}

	return Root[Node] = Find(Root[Node]);
}

void Union(int NodeA, int NodeB)
{
	int A = Find(NodeA);
	int B = Find(NodeB);

	int Min = min(A, B);
	Root[A] = Min;
	Root[B] = Min;
}

void InitTeam()
{
	cin >> N >> M >> K;

	for (int i = 0; i < N; ++i)
	{
		cin >> Candy[i];
		Root[i] = i;
	}

	for (int i = 0; i < M; ++i)
	{
		int A, B;
		cin >> A >> B;
		Union(A - 1, B - 1);
	}

	for (int i = 0; i < N; ++i)
	{
		if (Visited[i])
		{
			continue;
		}

		int R = Find(i);
		Visited[i] = true;
		++Team[TeamNum].first;
		Team[TeamNum].second += Candy[i];
		for (int j = i + 1; j < N; ++j)
		{
			if (R == Find(j))
			{
				Visited[j] = true;
				++Team[TeamNum].first;
				Team[TeamNum].second += Candy[j];
			}
		}

		++TeamNum;
	}
}

int DynamicProgramming()
{
	for (int i = 1; i <= TeamNum; ++i)
	{
		int Num = Team[i - 1].first;
		int Cdy = Team[i - 1].second;

		for (int n = 0; n < K; ++n)
		{
			if (Num <= n)
			{
				DP[i][n] = max(DP[i - 1][n], DP[i - 1][n - Num] + Cdy);
			}
			else
			{
				DP[i][n] = DP[i - 1][n];
			}
		}
	}

	return DP[TeamNum][K - 1];
}

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

	InitTeam();
	cout << DynamicProgramming();
}

 

분리집합 & 배낭 문제입니다.

풀이 과정은 다음과 같습니다.

 

1. 유니온 & 파인드 연산을 통해서 팀 정보 배열 생성

1-a. 팀 정보: 팀내의 인원, 사탕 개수

 

2. DP[팀개수][K] 행렬에 값 채우기

2-a. 일반적인 배낭 문제 풀이 방법 사용

2-b. K명의 사탕을 뺏으면 안되기 때문에 K-1까지만 채우기