안녕하세요! 테크지니어22입니다.
오늘은 프로그래머스 알고리즘 주사위 고르기를 풀어보도록 하겠습니다.
[문제 접근]
처음에는 for문으로 묵묵히 풀었습니다. 그러나 시간초과가 발생했습니다. 시간복잡도를 줄여야 한다는 것을 느꼈고, 어떤 알고리즘을 이용해야할지 생각했습니다. 가장 만만한게 이분 탐색이었는데 왜 이분 탐색을 써야하는지 당위성을 찾지 못했습니다. 이후 도움을 받았는데 아래의 예제표를 보고 어떤 알고리즘을 풀어야할 지 생각해보라는 힌트를 얻었습니다.
열을 보았을 때 b의 값보다 큰 a값이 나오는 순간만 찾으면 B가 특정값일때 이 값보다 큰 A의 값들의 개수를 셀 수 있습니다. 이를 통해 이분 탐색의 upper_bound 개념을 떠올릴 수 있었고 이를 활용하여 문제를 풀었습니다.
[풀이 전략]
1. 주사위에 번호를 매겨 dice_kind에 저장합니다.
2.조합에 사용할 0과 1로 구성된 벡터를 구성하고, permutation + do while을 구성합니다.
3. a와 b가 가져갈 주사위를 배분합니다.
4. a와 b는 정확히 절반씩 나누어 갖기 때문에, 6^n말고 6^(n-1) 경우만 확인합니다.
5. 10진법을 6진법으로 바꾸는 함수를 생성합니다.
6. 5를 이용하여 각 주사위로 뽑은 번호들의 합은 a와 b 각각 구한 다음에 asum_tot와 bsum_tot에 삽입합니다.
7. 이분탐색을 이용하여 a의 합이 b의 합보다 큰 경우를 셉니다.
8. 7에서 센 개수를 max_cnt와 비교하여 크면 max_cnt를 업데이트하고 answer에 a로 대치합니다.
[코드]
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> decimalToBase6(int decimal,int n ) {
vector<int> result(n) ;
int index = n -1 ;
while (decimal > 0) {
int remainder = decimal % 6;
result[index] = remainder;
decimal /= 6;
index--;
}
return result;
}
vector<int> solution(vector<vector<int>> dice) {
vector<int> answer;
vector<int> dice_kind ;
vector<int> tmp ;
int max_cnt = 0 ;
int dice_cnt = dice.size();
for(int i = 1 ; i <= dice_cnt ; i++){
dice_kind.push_back(i);
if( i <= dice_cnt / 2) {
tmp.push_back(1);
} else { tmp.push_back(0);}
}
do{
cout << "a";
vector<int> a ;
vector<int> b ;
for(int i = 0; i < dice_cnt; i++){
if(tmp[i] == 1) {
// A 주사위
a.push_back(dice_kind[i]);
} else {
// B 주사위
b.push_back(dice_kind[i]);
}
}
int tot_cnt = 1;
for(int i = 0 ; i < dice_cnt/2 ; i++) tot_cnt *= 6;
vector<int> asum_tot, bsum_tot ;
for(int j = 0 ; j < tot_cnt ; j++) {
auto t = decimalToBase6(j,dice_cnt/2);
int a_sum = 0 ;
int b_sum = 0 ;
for(int l = 0 ; l < dice_cnt/2 ; l++){
a_sum += dice[a[l]-1][t[l]];
b_sum += dice[b[l]-1][t[l]];
}
asum_tot.push_back(a_sum);
bsum_tot.push_back(b_sum);
}
sort(asum_tot.begin(), asum_tot.end());
sort(bsum_tot.begin(), bsum_tot.end());
int tmp_cnt = 0 ;
for(int i = 0 ; i < bsum_tot.size() ; i++) {
tmp_cnt += asum_tot.end() - upper_bound(asum_tot.begin(), asum_tot.end(), bsum_tot[i]);
}
if(max_cnt < tmp_cnt) {
max_cnt = tmp_cnt ;
answer = a;
}
} while(prev_permutation(tmp.begin(),tmp.end()));
return answer;
}
[배울 점]
- permutation함수 사용법 미숙
-> 항상 정렬을 하고 사용해야하고 조합을 할거면 파라미터로 1과 0로 구성된 벡터를 대입하기.
-> next랑 prev 사용법 숙지.
- 예제를 잘 살펴보고 패턴, 규칙을 살펴보고 어떤 알고리즘 개념을 이용해야할 지 유추하기. -> 단순 for문 밖에 생각나지 않는데 시간복잡도를 줄여야하는 상황에 좋음.
'알고리즘 > 프로그래머스' 카테고리의 다른 글
[프로그래머스/C++] 알고리즘 - 양궁대회 (0) | 2024.05.23 |
---|---|
[프로그래머스/C++] 알고리즘 - 도넛과 막대 그래프 (0) | 2024.05.16 |
[프로그래머스/C++] 알고리즘 - 두 큐 합 같게 만들기 (0) | 2024.05.12 |
[프로그래머스/C++] 알고리즘 - 순위 검색 (0) | 2024.05.05 |
[프로그래머스/C++] 알고리즘 - 택배 배달과 수거하기 (2) | 2024.04.30 |