Post

Algorithm - 백트래킹 문제 풀이

Algorithm

Algorithm - 백트래킹 문제 풀이

백준 문제를 풀고 리팩토링 해봤다.

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

첫 시도는 dfs로 구현하려고 시도했지만 실패했다. 허허

그래서 강의에서 배운 백트래킹 방식으로 풀이해봤다.

저번에 배웠던 백트래킹 방식처럼 재귀를 사용했고, 팀이 완성되면 두 팀의 점수 차이를 구한다.

첫 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
vector<bool> used;
vector<vector<int>> synerge;
stack<int> s;
int minDiff = INT_MAX;
int halfN;

int GetDiff() 
{ 
	int size = synerge.size();
	int diff = 0; for (int i = 0; 
	i < size; ++i) 
	{ 
		for (int j = 0; j < size; ++j)
		{ 
			if (used[i] == used[j]) 
			{ 
				if (used[i]) diff += synerge[i][j];
				else diff -= synerge[i][j];
			} 
		} 
	} 
	
	return abs(diff); 
}

void BackTrack() 
{ 
	if (s.size() == halfN) 
	{ 
		int diff = GetDiff(); 
		if (minDiff > diff) minDiff = diff;
		return; 
	} 
	
	for (int i = 0; i < used.size(); ++i) 
	{ 
		if (used[i]) continue; 
		used[i] = true; 
		s.push(i); 
		BackTrack(); 
		s.pop(); 
		used[i] = false; 
	} 
	
} 

int main() 
{ 
	int n; 
	cin >> n;
	
	halfN = n / 2; 
	synerge.resize(n); 
	used.resize(n, false); 
	
	for (int i = 0; i < n; ++i) 
	{ 
		for (int j = 0; j < n; ++j) 
		{ 
			int num; 
			cin >> num; 
			synerge[i].push_back(num);
		}
	} 
	
	BackTrack();
	cout << minDiff; 
}

개선할 부분이 있어서 몇 가지 수정해봤다.

GetDiff 함수 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int GetDiff() 
{ 
	int size = synerge.size();
	int diff = 0; for (int i = 0; 
	i < size; ++i) 
	{ 
		for (int j = 0; j < size; ++j)
		{ 
			if (used[i] == used[j]) 
			{ 
				if (used[i]) diff += synerge[i][j];
				else diff -= synerge[i][j];
			} 
		} 
	} 
	
	return abs(diff); 
}

두 팀의 시너지를 구해 차이를 구하는 방식으로 구현했었다.
이 방식은 i와 j가 같은 팀이면 연산이 가능하기에 필요없는 반복이 들어있다.

때문에 미리 팀마다 배열을 만들어 분류하여 불필요한 조건 검사를 제외했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int GetDiff()
{
	int size = synerge.size();
	int diff = 0;

	vector<int> startTeam, linkTeam;

		for (int i = 0; i < size; ++i) // 팀마다 배열로 분류
	{
		if (used[i]) startTeam.push_back(i);
		else linkTeam.push_back(i);
	}


	for (int i = 0; i < halfN; ++i) // 한 팀의 팀원 수는 halfN
	{
		for (int j = 0; j < halfN; ++j)
		{
			diff += synerge[startTeam[i]][startTeam[j]] - synerge[linkTeam[i]][linkTeam[j]]; // 두 팀의 시너지 차이를 누적 계산
		}
	}

	return abs(diff);
}

Index 추가 및 Stack 제거

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void BackTrack() 
{ 
	if (s.size() == halfN) 
	{ 
		int diff = GetDiff(); 
		if (minDiff > diff) minDiff = diff;
		return; 
	} 
	
	for (int i = 0; i < used.size(); ++i) 
	{ 
		if (used[i]) continue; 
		used[i] = true; 
		s.push(i); 
		BackTrack(); 
		s.pop(); 
		used[i] = false; 
	} 
	
} 

백트래킹 하는 함수에서 깊이를 검사하기 위해 스택을 사용했었다.
하지만 단순히 깊이만 저장한다면 int만 사용하면 되기에 수정했다.

1
2
3
4
5
6
7
8
9
10
void BackTrack(int index, int count)
{
	if (count == halfN)
	{
		int diff = GetDiff();
		if (minDiff > diff) minDiff = diff;
		return;
	}
	// 아래는 생략
}

매개변수에 넣어서 따로 뺄 필요가 없다.

또한 새로운 팀원을 추가할 때도

1
2
3
4
5
6
7
8
9
	for (int i = 0; i < used.size(); ++i) 
	{ 
		if (used[i]) continue; 
		used[i] = true; 
		s.push(i); 
		BackTrack(); 
		s.pop(); 
		used[i] = false; 
	} 

왼쪽부터 탐색했기 때문에 이미 지나간 자리는 검사할 필요가 없어 수정했다.

1
2
3
4
5
6
7
8
9
10
	for (int i = index; i < used.size(); ++i)
	{
		if (used[i]) continue;

		used[i] = true;

		BackTrack(i, count + 1);
		
		used[i] = false;
	}

수정본

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <string>
#include <iostream>
#include <vector>

using namespace std;

vector<bool> used;
vector<vector<int>> synerge;

int minDiff = INT_MAX;
int halfN;

int GetDiff()
{
	int size = synerge.size();
	int diff = 0;

	vector<int> startTeam, linkTeam;

	for (int i = 0; i < size; ++i)
	{
		if (used[i]) startTeam.push_back(i);
		else linkTeam.push_back(i);
	}


	for (int i = 0; i < halfN; ++i)
	{
		for (int j = 0; j < halfN; ++j)
		{
			diff += synerge[startTeam[i]][startTeam[j]] - synerge[linkTeam[i]][linkTeam[j]];
		}
	}

	return abs(diff);
}

void BackTrack(int index, int count)
{
	if (count == halfN)
	{
		int diff = GetDiff();
		if (minDiff > diff) minDiff = diff;
		return;
	}

	for (int i = index; i < used.size(); ++i)
	{
		if (used[i]) continue;

		used[i] = true;

		BackTrack(i, count + 1);
		
		used[i] = false;
	}
}

int main()
{
	int n;
	cin >> n;

	halfN = n / 2;
	synerge.resize(n, vector<int>(n));
	used.resize(n, false);

	for (int i = 0; i < n; ++i)
	{
		for (int j = 0; j < n; ++j)
		{
			int num;
			cin >> num;
			synerge[i][j] = num;
		}
	}

	BackTrack(0, 0);

	cout << minDiff;
}
This post is licensed under CC BY 4.0 by the author.