본문 바로가기
Algorithm/BOJ

[BOJ]14889번: 스타트와 링크 (c++)

by HBGB 2020. 5. 21.

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

 

14889번: 스타트와 링크

예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.

www.acmicpc.net

 

방법 1: 백트랙킹

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

const int MAX = 20;
int min_dif = 1000;
int S[MAX][MAX];
int N;

// 점수 차이 구하기
int get_point_dif(vector<int>& team_s, vector<int>& team_l)
{
	int sum_s = 0;
	int sum_l = 0;
	for (int i = 0; i < team_s.size(); ++i)
	{
		for (int j = 0; j < team_s.size(); ++j)
		{
			sum_s += S[team_s[i]][team_s[j]];
			sum_l += S[team_l[i]][team_l[j]];
		}
	}
	return abs(sum_s - sum_l);
}

void dfs(vector<int> & team_s, vector<int> &team_l, int index)
{
	if (index == N)
	{
		// 각 팀이 모두 N / 2명이 아니면 X
		if (team_s.size() != N / 2 || team_l.size() != N / 2)
		{
			return;
		}

		// 최소 점수차이 구하기
		min_dif = min(min_dif, get_point_dif(team_s, team_l));

		return;
	}

	// index번째의 선수가 team_s에 속했을 때
	team_s.push_back(index);
	dfs(team_s, team_l, index + 1);
	team_s.pop_back();

	// index번째의 선수가 team_l에 속했을 때
	team_l.push_back(index);
	dfs(team_s, team_l, index + 1);
	team_l.pop_back();
}

int main()
{
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	// 입력
	cin >> N;
	for (int i = 0; i < N; ++i)
	{
		for (int j = 0; j < N; ++j)
		{
			cin >> S[i][j];
		}
	}

	// dfs - 백트랙킹으로 선수 조합해서 팀만들기
	vector<int> team_s, team_l;
	dfs(team_s, team_l, 0);

	cout << min_dif;

	return 0;
}

 

방법 2: 비트마스크

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

// 두 팀의 점수 차이 구하기
int get_dif(vector<vector<int>> &S, int bitset)
{
	int team_s = 0;
	int team_l = 0;
	int len = S.size();

	for (int i = 0; i < len; ++i)
	{
		for (int j = i; j < len; ++j)
		{
			// 비트가 1이면 team_s에 점수 합산
			if (bitset & ((1 << i) | (1 << j)))
			{
				team_s += S[i][j] + S[j][i];
			}
			// 비트가 0이면 team_l에 점수 합산
			if (~bitset & ((1 << i) | (1 << j)))
			{
				team_l += S[i][j] + S[j][i];
			}
		}
	}
	// 차이의 절대값 반환
	return abs(team_s - team_l);
}

// 1의 개수가 N / 2이면 true
bool check(int bitset, int N)
{
	int cnt = 0;
	for (int i = 0; i < N; ++i)
	{
		if (bitset & (1 << i))
		{
			++cnt;
		}
	}
	return cnt == N / 2;
}

int main()
{
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	// 입력
	int N;
	cin >> N;
	vector<vector<int>> S(N, vector<int>(N));
	for (int i = 0; i < N; ++i)
	{
		for (int j = 0; j < N; ++j)
		{
			cin >> S[i][j];
		}
	}

	// 비트마스크로 팀 조합 구하기
	int min_dif = 2000;
	for (int i = 1; i < (1 << N); ++i)
	{
		// 1의 개수가 N / 2이면 true
		if (check(i, N))
		{
			// 차이의 최소값 구하기
			min_dif = min(min_dif, get_dif(S, i));
		}
	}

	cout << min_dif;

	return 0;
}

 

방법 3 : 순열

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

enum {A, B};

int main()
{
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	// 입력
	int N;
	cin >> N;
	vector<vector<int>> point(N, vector<int>(N));
	for (int i = 0; i < N; ++i)
	{
		for (int j = 0; j < N; ++j)
		{
			cin >> point[i][j];
		}
	}
	
	// mem벡터의 앞 절반은 0, 뒤 절반은 1로 초기화
	vector<int> mem(N, 1);
	for (int i = 0; i < N / 2; ++i)
	{
		mem[i] = 0;
	}

	// 순열로 멤버들이 팀을 선택하는 다양한 경우의 수 구해서 점수차이 계산
	int min_dif = 987654321;
	do {
		vector<int> teamA, teamB;
		for (int i = 0; i < N; ++i)
		{
			// 0 -> team A, 1 -> team B
			if (mem[i] == A)
			{
				teamA.push_back(i);
			}
			else
			{
				teamB.push_back(i);
			}
		}
		
		int pA = 0, pB = 0;
		for (int i = 0; i < N / 2; ++i)
		{
			for (int j = 0; j < N / 2; ++j)
			{
				pA += point[teamA[i]][teamA[j]];
				pB += point[teamB[i]][teamB[j]];
			}
		}

		// 최소차이 구하기
		min_dif = min(min_dif, abs(pA - pB));
	
	} while (next_permutation(mem.begin(), mem.end()));

	cout << min_dif;

	return 0;
}

 

속도 순위는 1. 백트랙킹 2. 순열 3. 비트마스크 순으로 빠르다.

 

 

 

TIP_백트랙킹

이 문제는 X를 팀 A에 포함시킬거냐, 아니면 팀 B에 포함시킬거냐를 결정해야 하는 문제이다. 

다른 백트랙킹 문제와 마찬가지로, 

선택 A를 했다가 선택 B로 넘어갈 때는 원상복구를 잘 해줘야 한다.

 

 

TIP_비트마스크

팀이 2개 밖에 없으므로 , 비트마스크를 사용할 수 있다.

다만 팀원 수를 카운팅 하는 메소드를 따로 작성해주어야 하는 번거로움이 있다.

ex) N이 4일때 1000은 팀원 수가 1 : 3 이므로 X

'Algorithm > BOJ' 카테고리의 다른 글

[BOJ]2529번: 부등호 (c++)  (0) 2020.05.21
[BOJ]15661번: 링크와 스타트 (c++)  (0) 2020.05.21
[BOJ]14501번: 퇴사 (c++)  (0) 2020.05.20
[BOJ]1759번: 암호 만들기 (c++)  (0) 2020.05.20
[BOJ]1260번: DFS와 BFS(c++)  (0) 2020.05.12

댓글