출처: 프로그래머스 코딩 테스트 연습
https://school.programmers.co.kr/learn/courses/30/lessons/12952
1차 시도
public int solution(int n)
{
int answer = 0;
// 한 줄에 한개의 퀸만 들어갈 수 있다.
// 우선 0 ~ n - 1 로 만들 수 있는 순열을 만든다.
int[] arr = Enumerable.Range(0, n).ToArray();
var list = new List<int[]>();
Perm(list, arr, 0, n, n);
// 퀸은 자신의 위치에서 가로가 k차이 나면 세로 절대값 k차이는 같이 배치될 수 없다.
foreach(int[] nums in list)
{
if(IsValid(nums, n))
++answer;
}
return answer;
}
private bool IsValid(int[] array, int n)
{
for(int x = 0; x < n; ++x)
{
for(int k = 1; k < n; ++k)
{
if(x + k < n)
{
if(Math.Abs(array[x] - array[x + k]) == k)
return false;
}
}
}
return true;
}
private void Perm(List<int[]> list, int[] arr, int depth, int n, int k)
{
if(depth == k)
{
list.Add(arr.Clone() as int[]);
return;
}
for(int i = depth; i < n; ++i)
{
Swap(arr, i, depth);
Perm(list, arr, depth + 1, n, k);
Swap(arr, i, depth);
}
}
private void Swap(int[] arr, int a, int b)
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
1. 십자 형태 제외하기
- 세로줄 당 하나의 퀸이 들어갈 수 있음.
- 0 ~ n -1 로 순열을 만들기로 함.
2. 대각선 형태 제외하기
- 가로가 k만큼 차이날 때 y도 k만큼 차이 나면 대각선이니 제외한다.
2차 시도
public int solution(int n)
{
int answer = 0;
// 한 줄에 한개의 퀸만 들어갈 수 있다.
// 0 ~ n - 1 로 만들 수 있는 순열을 만들고 체크.
int[] arr = Enumerable.Range(0, n).ToArray();
Perm(arr, 0, n, ref answer);
return answer;
}
private void Perm(int[] arr, int depth, int n, ref int answer)
{
if(depth == n)
{
if(IsValid(arr, n))
++answer;
return;
}
for(int i = depth; i < n; ++i)
{
Swap(arr, i, depth);
Perm(arr, depth + 1, n, ref answer);
Swap(arr, i, depth);
}
}
private void Swap(int[] arr, int a, int b)
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
// 가로가 k만큼 차이날 때 y도 k만큼 차이나면 대각선이니 제외한다.
private bool IsValid(int[] array, int n)
{
for(int x = 0; x < n; ++x)
{
for(int k = 1; k < n - x; ++k)
{
if(Math.Abs(array[x] - array[x + k]) == k)
return false;
}
}
return true;
}
약간의 최적화 시도. 별도로 리스트에 담지 않고 바로바로 IsValid를 체크하도록 했다.
거의 시간이 절반으로 줄었지만, 11번 테스트는 아마 최대치 (n = 12) 인 듯하다.
나의 풀이
public int solution(int n)
{
int answer = 0;
// 한 줄에 한개의 퀸만 들어갈 수 있다.
// 0 ~ n - 1 로 만들 수 있는 순열을 만들고 체크.
int[] arr = Enumerable.Range(0, n).ToArray();
Perm(arr, 0, n, ref answer);
return answer;
}
private void Perm(int[] arr, int depth, int n, ref int answer)
{
if(depth == n)
{
++answer;
return;
}
for(int i = depth; i < n; ++i)
{
Swap(arr, i, depth);
if(IsValid(arr, depth))
Perm(arr, depth + 1, n, ref answer);
Swap(arr, i, depth);
}
}
private void Swap(int[] arr, int a, int b)
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
// 가로가 k만큼 차이날 때 y도 k만큼 차이나면 대각선이니 제외한다.
// 아직 depth까지만 채워져 있기때문에 가로줄 기준 depth까지만 체크해도된다.
private bool IsValid(int[] arr, int depth)
{
for(int x = 0; x < depth; ++x)
{
if(Math.Abs(x - depth) == Math.Abs(arr[x] - arr[depth]))
return false;
}
return true;
}
순열을 다 만들고 IsValid 체크하는 게 아니고,
가지를 만들기 전에 우선 체크해서 필요 없는 부분을 제거하는.. A*의 휴리스틱 같은 느낌.
아무래도 재귀다 보니 체크를 먼저 해주면 성능이 극적으로 향상된다.
다른 사람 풀이
public int solution(int n)
{
int answer = 0;
// 한 줄에 한개의 퀸만 들어갈 수 있다.
var arr = new int[n];
BackTracking(arr, 0, n, ref answer);
return answer;
}
private void BackTracking(int[] arr, int depth, int n, ref int answer)
{
if(depth == n)
{
++answer;
return;
}
for(int i = 0; i < n; ++i)
{
arr[depth] = i;
if(IsValid(arr, depth))
BackTracking(arr, depth + 1, n, ref answer);
}
}
// depth까지만 계산한다.
private bool IsValid(int[] array, int depth)
{
for(int x = 0; x < depth; ++x)
{
if(array[x] == array[depth]) return false;
if(Math.Abs(array[x] - array[depth]) == Math.Abs(depth - x)) return false;
}
return true;
}
백트레킹 방식으로 풀이.
내가 풀었던 순열 자체도 백트레킹의 일종이라서 비슷하다.
성능은 비슷하겠지만 이쪽이 좀 더 간결하기 때문에 다음엔 백트레킹 쪽으로 풀어야겠다.
'🛡️ 코딩테스트 > 🛡️ 코테 : 프로그래머스' 카테고리의 다른 글
C# 조이스틱 - 그리디 DFS / 프로그래머스 [Lv.2] (0) | 2023.04.06 |
---|---|
C# 미로 탈출 - BFS 길찾기 / 프로그래머스 [Lv.2] (0) | 2023.04.05 |
C# 시소 짝꿍 - 조합 / 프로그래머스 [Lv.2] (0) | 2023.04.03 |
C# 피로도 - DFS 순열 / 프로그래머스 [Lv.2] (0) | 2023.04.03 |
C# 테이블 해시 함수 - 정렬 / 프로그래머스 [Lv.2] (0) | 2023.04.02 |