일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 백준
- Django Rest Framework
- serializer
- 독서
- 코딩 공부
- DP
- Django Restful API
- 정보 보안
- 추리 소설
- 백엔드 개발
- KITRI
- webhacking.kr
- 일본 소설
- 가가 형사 시리즈
- 백준 알고리즘
- Django CRUD
- best of the best
- 히가시노 게이고
- 백준알고리즘
- 알고리즘
- 백엔드
- bob
- 삼성 SW 역량 테스트
- 웹 개발
- 코딩
- bob 9기 후기
- 웹 해킹
- 동적 프로그래밍
- Blind SQL Injection
- 코딩공부
- Today
- Total
요모조모 ʚɞ
[C] 백준 14889번 스타트와 링크 본문
안녕하세요 :-)
오늘도 삼성 SW 역량 테스트 기출문제를 풀어보았습니다!
.
문제의 입력 범위가 4 ≤ N ≤ 20으로 주어졌기 때문에
저는 이번에도 재귀 함수를 사용하여 풀어보았습니다!
이 문제를 수학적으로 접근을 해보자면 n명의 사람을 2개의 팀으로 나누는 것이기 때문에
nC2를 구하는 것과 같다고 할 수 있습니다.
만약 n이 4라고 한다면 아래의 그림과 같이 표현할 수 있습니다.
(아래의 표에서 O는 스타트 팀, △는 링크 팀을 의미합니다!)
여기서 뭔가 공통된 부분이 보이시나요?
위의 그림과 같이 중간의 빨간 선을 기준으로 형식이 동일한 케이스가 있다는 것을 알 수 있습니다.
따라서 우리는 빨간 선을 기준으로 위의 케이스만 구한 다음, 해당 케이스를 빨간 선 기준 아래의 케이스로 한번 더 고려해주면 됩니다.
(O가 스타트 팀인 경우와 링크 팀인 경우 이 2가지로 나누어 생각하면 되겠죠?)
그럼 빨간 선을 기준으로 위에 있는 케이스는 위의 그림과 같이 트리로 표현을 할 수 있고,
적절한 종료 조건을 사용하여 불필요한 검사를 줄일 수 있습니다.
제가 설정한 종료 조건은 아래와 같습니다.
depth == n-1 || cnt == n/2일 때 혹은 depth-cnt == n/2일 때
여기서 depth는 트리의 depth를 의미하고 저는 0에서부터 시작했기 때문에 n-1까지만 검사를 합니다.
그리고 cnt는 특정 팀의 인원을 의미하며, 각 팀의 인원은 n/2로 같아야 하기 때문에 cnt가 n/2일 때까지만 검사를 하였습니다.
마지막으로 depth-cnt == n/2 조건은 불필요한 케이스를 줄이기 위한 추가 조건으로,
이것 역시 각 팀의 인원이 n/2명이 넘지 않도록 설정해주었습니다.
.
이를 바탕으로 작성한 코드는 아래와 같습니다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define minus(x, y) x>y?x-y:y-x
int n, cap[20][20];
int team[20]; // 스타트 팀와 링크 팀을 구분 짓기 위한 배열
int index = 0; // team 배열의 index
int min = 100;
void calculate() {
int a1 = 0, b1 = 0; // 1이 스타트 팀일 때
int a2 = 0, b2 = 0; // 0이 스타트 팀일 때
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i == j)
continue;
if (team[i] == 1 && team[j] == 1) {
a1 += cap[i][j];
a2 += cap[j][i];
}
if (team[i] == 0 && team[j] == 0) {
b1 += cap[i][j];
b2 += cap[j][i];
}
}
}
int gap1 = minus(a1, b1);
int gap2 = minus(a2, b2);
if (gap1 < min)
min = gap1;
if (gap2 < min)
min = gap2;
}
void comb(int depth, int isChoose, int cnt) {
if (isChoose == 1)
team[depth] = 1;
else
team[depth] = 0;
if (depth - cnt == n / 2)
return;
else {
if (depth == n - 1 || cnt == n / 2) {
calculate();
return;
}
}
// choose O
comb(depth + 1, 1, cnt + 1);
// choose X
comb(depth + 1, 0, cnt);
return;
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
scanf("%d", &cap[i][j]);
comb(0, 1, 1);
printf("%d", min);
}
저는 team이라는 배열을 사용하여 전체 인원을 2가지(0 or 1)로 구분하였습니다.
이를 구분짓기 위한 인자로는 isChoose를 사용하였습니다.
그리고 앞서 언급한 트리의 탐색 종료 조건을 활용하여 재귀 함수를 작성하였습니다.
저는 전체 케이스의 절반을 제거하고 시작하였기 때문에 재귀 함수의 시작을 comb(0, 1, 1)로 설정하였습니다.
마지막으로 적절한 종료 조건에 다다른 케이스(전체 인원이 정확히 2개의 팀으로 나누어진 경우)는 calculate() 함수를 통해 팀 간의 능력치 차이를 계산하였습니다.
이때 앞서 제거한 절반의 케이스(빨간 선 기준 아래의 경우)를 함께 계산해주기 위해 team 배열의 값이 1일 때를 스타트 팀일 때, 링크 팀일 때로 나누어 2가지 경우로 계산해주었습니다.
.
평소 문제의 입력 범위가 작으면 재귀함수로 풀어버리려는 습관(?)이 있어서
이번에도 재귀함수로 먼저 접근해보았습니다.
하지만 문제를 풀면서도 뭔가 더 쉽고 간단하게 풀 수 있는 방법이 있을 것이라는 생각이 들더라구요 ㅜ
그래서 다음번에는 이 문제를 좀 더 가볍게? 더 빠르게 만들어보려고 합니다 ㅎ..
to be continued...☆
'코딩 > C' 카테고리의 다른 글
[C] 백준 14891번 톱니바퀴 (0) | 2020.11.29 |
---|---|
[C] 백준 11723번 집합 (0) | 2020.11.22 |
[C] 백준 14501번 퇴사 (0) | 2020.10.12 |
[C] 백준 13458번 시험 감독 (0) | 2020.10.12 |
[C] 백준 17471번 : 게리맨더링 (0) | 2020.05.04 |