1. 문제

1) 문제 링크

    : https://programmers.co.kr/learn/courses/30/lessons/17677

 

코딩테스트 연습 - [1차] 뉴스 클러스터링

뉴스 클러스터링 여러 언론사에서 쏟아지는 뉴스, 특히 속보성 뉴스를 보면 비슷비슷한 제목의 기사가 많아 정작 필요한 기사를 찾기가 어렵다. Daum 뉴스의 개발 업무를 맡게 된 신입사원 튜브

programmers.co.kr

 

2) 설명

자카드 유사도를 활용해 두 문자열의 유사도를 비교하는 문제다

자카드 유사도에 대한 자세한 설명은 문제 링크에 있다

 

2. 풀이

1) 알고리즘

    : 집합?

 

2) 풀이 설명

C++로 sstream 사용해서 풀어도 되긴 하는데 파이썬으로 풀기에 너무 적합한 문제 같아서 그냥 파이썬으로 풀어봤다.

일단 소문자 대문자를 가리지 않기 때문에 문자열을 다 소문자로 변환했다.

그리고 각 문자열을 두 글자씩 떼서 리스트를 만든다. 이때 특수문자가 포함되어 있으면 건너뛴다.

이제 n(교집합) / n(합집합) 을 구하면 된다.(마지막에 한 번만 곱해주면 되기 때문에 65536을 곱하는 건 일단 생략하겠다...)

n(A U B) = n(A) + n(B) - n(A ^ B) // n(A ^ B)는 A B 교집합

공식을 이용한다.

교집합을 구하는 건 str1_arr(str1에서 두 글자씩 떼어낸 문자열 집합)에 있는 문자열 중 str2_arr(str2에서 두 글자씩 떼어낸 문자열 집합)에 있는 것들만 카운팅해준다. 카운팅하면 str2_arr에서 그 문자열은 빼줘야 한다. 왜냐하면 다음과 같은 경우를 방지하기 위해서다.

str1_arr = {1, 1, 1}

str2_arr = {1, 2, 3}

일 때 처음 1을 카운팅해주고 str2_arr에서 빼주지 않으면 두 번째, 세 번째 1에서도 str2_arr에 1 값이 있기 때문에 교집합의 개수가 3이 되기 때문이다.

 

여튼 그렇게 교집합의 개수를 구했으면 공식 따라서 자카드 유사도를 구해주면 된다.

 

 

3. 코드

더보기
def solution(str1, str2):
    mul_val = 65536
    
    # 소문자 변환
    str1 = str1.lower()
    str2 = str2.lower()
    
    # 두글자씩 떼어서 배열로 변환
    str1_arr = [ str1[i : i + 2] for i in range(len(str1) - 1) if str1[i : i + 2].isalpha() ]
    str2_arr = [ str2[i : i + 2] for i in range(len(str2) - 1) if str2[i : i + 2].isalpha() ]
    
    union_count = len(str1_arr) + len(str2_arr) # (A U B) = (A) + (B) - (A ^ B) 공식 사용 : 교집합 개수 구한 후 빼줘야 함
    # 교집합 개수 구하기
    intersection_count = 0
    for x in str1_arr:
        if x in str2_arr:
            intersection_count += 1
            str2_arr.remove(x)
    
    # 합집합 개수 구하기
    union_count -= intersection_count # 교집합 개수 빼주기
    
    if union_count == 0:
        return mul_val
    else:
        return int(intersection_count / union_count * mul_val)

 

 

4. 느낀점

리스트 컴프리헨션 최고!

문자열에 isalpha()라는 함수가 있다는 걸 처음 알았다..

Counter라는 걸 사용하면 교집합 합집합을 더 쉽게 구할 수 있는 것  같다. 틈 나면 공부해보자!(보통 이러면 안 한다)

'알고리즘 > 프로그래머스' 카테고리의 다른 글

[LV4] 4단 고음  (0) 2021.08.19
[LV3] 불량 사용자  (0) 2021.08.13
[LV4] 호텔 방 배정  (0) 2021.08.13
[LV2] 거리두기 확인하기  (0) 2021.07.29

1. 문제

1) 문제 링크

    : https://programmers.co.kr/learn/courses/30/lessons/1831

 

코딩테스트 연습 - 4단 고음

4단 고음 I'm in my dream~↗ ~↗ ~↗ IU는 본인의 장기인 3단 고음으로 유명하다. 그러던 그녀가 어느 날 4단 고음을 성공했고 그녀의 고음은 학계에서 연구가 될 만큼 유명해졌다 [1]. [1] 견두헌, 배명

programmers.co.kr

 

2) 설명

문제를 읽어보니 *++을 이용해서 n을 만들어내는 경우의 수를 구하는 문제다.(*의 의미는 곱하기 3, +는 더하기 1이다)

다만 여기에 규칙이 몇 개가 있다.

 

규칙 1 : *++은 반드시 *가 나온 후에 + 두 개가 나와야 한다.

규칙 2 : *++ 계산을 하는 중간에 새로운 *++이 시작할 수도 있다.

 

예를 들어

1. *++*++은 올바르다. 이대로 계산을 하면

* + + * + +
3 4 5 15 16 17

이렇게 17을 만들 수 있다.

 

2. 반면 *+++*+ 은 올바르지 않다.

규칙 1에 위배된다.

 

3. *+*+++은 올바르다.

규칙 3에 따라 *+ ( *++) + 이렇게 중간에 새로운 *++이 시작한 것이다.

* + * + + +
3 4 12 13 14 15

이렇게 15를 만들 수 있다.

 

 

2. 풀이

1) 알고리즘

    : 재귀

 

2) 풀이 설명

처음에는 재귀 반복으로 해결하려 했는데 시간초과가 났다ㅠㅠ 그래서 다른 풀이들을 약간 참고했다.

 

이 풀이는 반드시 * 뒤에 ++가 나와야 한다는 규칙을 이용한다.

모든 *가 +에 우선해야 한다는(ex : ******++++++...) 의미가 아니라 *++ 쌍을 이루는 3단 고음이 * + + 순서대로 나와야 한다는 것이다.

dfs(재귀함수)에는 만들고 싶은 수 n과 남아있는 +의 수 plusCnt를 인자로 받는다.

예를 들어 20을 만들고 싶고 +가 4개가 나온 상황이라면 dfs(20, 4)가 된다.

재귀적으로 dfs함수를 호출해 n = 3, plusCnt = 2까지 줄일 수 있는 경우의 수를 세는 게 문제를 푸는 방법이다.

n이 3, plusCnt가 2라는 말은 *++를 의미한다.

n = 3, plusCnt = 2까지 못 만들면 해당 {n, plusCnt} 상태에서는 규칙에 맞게 *++을 만들 수 없다.

 

또한 (앞으로 나올 수 있는 *의 최대 개수 < 반드시 나와야 할 *의 최소 개수) 라면 더이상 재귀함수를 진행하는 것이 무의미하다. 왜냐면 어떻게 진행을 해도 최소한 나와야 하는 *의 개수를 만들 수 없게 되기 때문이다. -> log(n)/log(3) < (plusCnt + 1)/2 이면 진행을 멈춘다. ( log(n)/log(3)은 (로그 3의 n), 즉 3을 몇 번 제곱해야 n이 되냐는 말이다) (plusCnt + 1)인 이유는 plusCnt가 홀수 개일 때를 대비하기 위함이다. (ex : plusCnt = 5일 때 *는 최소 3개가 나와야 한다. 그러려면 (5 + 1)/2로 해줘야 함)

 

n이 3으로 나누어 떨어진다면 + plusCnt가 2 이상이라면 dfs(n/3, plusCnt - 2)를 호출한다.

n을 3으로 나눈다는 것은 *가 하나 등장했다는 의미이고 plusCnt가 2개 이상 있어야 규칙 1을 위반하지 않을 수 있다.

 

n이 3으로 나누어 떨어지든 안 떨어지든 dfs(n - 1, plusCnt + 1)을 호출한다.

n에서 1을 뺀다는 것은 +를 하나 늘렸다는 말이므로 plusCnt + 1을 해주는 것이다.

 

이 과정을 n은 3, plusCnt는 2가 될 때까지 반복해주면 된다.

 

참고로 첫 호출은 dfs(n - 2, 2)로 해주면 된다. 왜냐하면 마지막 두 개는 반드시 ++이기(plusCnt = 2) 때문이다.(그냥 dfs(n, 0)해도 상관 없다)

 

예를 들어서 n = 13일 때 어떻게 진행되는지 보자.

dfs(11, 2) 호출

-> 11%3 != 0이므로 dfs(10, 3)만 호출

    -> 10%3 != 0이므로 dfs(9, 4)만 호출

        -> 9%3 == 0이고 4(plusCnt) >= 2이므로 dfs(3, 2) 호출

            -> n == 3, plusCnt == 2 이니까 return 1

        -> dfs(8, 5) 호출

            -> log(8)/log(3)은 2보다 작고 (5(plusCnt) + 1)/2)인 3보다 작으므로 return 0

        : 1 + 0 해서 return 1

    : return 1

: reuturn 1

 

이런 식으로 진행된다.

 

3. 코드

더보기
#include <math.h>

// 전역 변수를 정의할 경우 함수 내에 초기화 코드를 꼭 작성해주세요.
using namespace std;

int dfs(int n, int plusCnt){
    if(n < 1 || log(n)/log(3) < (plusCnt + 1)/2){
        // log(n)/log(3)	: 앞으로 나올 수 있는 최대한의 * 횟수(n이 되기 위해 3을 거듭제곱해야 하는 횟수)
        // (plusCnt + 1)/2	: *가 나와야 하는 최소한의 횟수
  		// 앞으로 *가 최대로 나오는 경우(log(n)/log(3)) 가 반드시 나와야 하는 *의 횟수((plusCnt + 1)/2) 보다 작다면 더 재귀를 진행해봤자 무의미함으로 return
        return 0;
    }
    if(n == 3 && plusCnt == 2){
        // * : 하나, + : 둘 이므로 1 return
        return 1;
    }
    
    int ret = 0;
    if(n%3 == 0 && plusCnt >= 2){
    	//3으로 나눠지고 남은 +가 두 개 이상이어야 *가 나올 수 있음
        ret += dfs(n/3, plusCnt - 2);
	}
    ret += dfs(n - 1, plusCnt + 1); //+ 하나를 늘리는 경우
    
    return ret;
}

int solution(int n) {
    return dfs(n - 2, 2); // 시작 시 맨 마지막 두 개는 무조건 ++이어야 하므로 plusCnt를 2로 시작
}

 

 

4. 느낀점

오랜만에 log를 봤다...

종종 사용하면 좋을 것 같다.

'알고리즘 > 프로그래머스' 카테고리의 다른 글

[LV2] 뉴스 클러스터링  (0) 2021.08.26
[LV3] 불량 사용자  (0) 2021.08.13
[LV4] 호텔 방 배정  (0) 2021.08.13
[LV2] 거리두기 확인하기  (0) 2021.07.29

1. 문제

1) 문제 링크

    : https://programmers.co.kr/learn/courses/30/lessons/64064

 

코딩테스트 연습 - 불량 사용자

개발팀 내에서 이벤트 개발을 담당하고 있는 "무지"는 최근 진행된 카카오이모티콘 이벤트에 비정상적인 방법으로 당첨을 시도한 응모자들을 발견하였습니다. 이런 응모자들을 따로 모아 불량

programmers.co.kr

 

2) 설명

    user id 배열과 불량 사용자 id 배열이 주어진다.

불량 사용자 id는 중간중간 *(wild mask)가 들어갈 수 있다.(길이는 반드시 맞아야 한다)

이때 불량 사용자가 될 수 있는 user id들의 집합의 수를 구하면 된다.

 

2. 풀이

1) 알고리즘

    : set, bit연산

 

2) 풀이 설명

일단 user_id 배열이나 banned_id 배열이나 8개 이하라서 그냥 조합을 다 살펴보면서 세면 될 것 같았다.

이 조합의 수 세기를 재귀적으로 풀기 위해 전역변수로 my_user_id, my_banned_id, my_answer, id_set 들을 선언해주었다.

(매번 함수 인자로 넘기기 귀찮아서..)

그리고 user_id가 banned_id 에 해당하는지를 확인하는 is_matched함수도 하나 만들었다.(wild mask 때문에 새로 만들어줘야 했다.)

 

calculate_comb_num 함수는 banned_id의 index를 인자로 받아서 거기에 맞는 user_id들을 선택한 다음에  다음에 체크할 banned_id의 순번(banned_idx)와 해당 user_id를 선택했다는 사실(id_bit_check에 선택한 user_id의 index를 &연산)을 인자로 해서 자기 자신을 호출한다.

이 과정은 banned id를 다 셀 때까지 계속되고, 그때 구한 조합의 집합(id_bit_check)가 id_set에 없을 때 myAnswer 값을 1 증가시켜준다.(id_set에 있다는 말은 이전에 이미 카운팅했다는 말임으로)

 

3. 코드

더보기
// 풀이

#include <string>
#include <vector>
#include <set>

using namespace std;

vector<string> my_user_id, my_banned_id;
int my_answer = 0;
set<int> id_set;

bool is_matched(string id, string form){
    //불량사용자 form에 id가 맞는지 확인하는 함수
    if(id.length() != form.length())
        return false;
    for(int i=0;i<id.length();i++){
        if(id[i] == '*' || form[i] == '*')
            continue;
        if(id[i] != form[i])
            return false;
    }
    return true;
}

void calculate_comb_num(int banned_id_idx, int id_bit_check){
    /*
        @banned_id_idx        = 다음에 택할 불량사용자 index
        @id_bit_check = bit로 표현한 불량사용자로 선택된 user 집합
    */
    if(banned_id_idx == my_banned_id.size()){ 
        //불량사용자를 다 배정했으면 진입
        if(id_set.find(id_bit_check) != id_set.end()){
            //이미 셌던 집합이면 return
            return;
        } 
        my_answer++; //조합 개수 증가
        id_set.insert(id_bit_check); //id_set에 추가
        return;
    }
    for(int i=0;i<my_user_id.size();i++){
        if(!(id_bit_check & (1 << i)) && is_matched(my_user_id[i], my_banned_id[banned_id_idx])){
            //불량 사용자로 선택했던 user가 아니면 + 불량사용자 조건에 해당하면...
            calculate_comb_num(banned_id_idx + 1, id_bit_check | (1 << i));
        }
    }
}

int solution(vector<string> user_id, vector<string> banned_id) {
    my_user_id = user_id;
    my_banned_id = banned_id;
    
    calculate_comb_num(0, 0);
    
    return my_answer;
}

 

 

4. 느낀점

문제에서 스네이크 표기법 써서 그냥 통일해서 쓰는데 언더바 치기 귀찮다...

카멜표기법이 게으른 나에게 더 맞는 것 같다..

'알고리즘 > 프로그래머스' 카테고리의 다른 글

[LV2] 뉴스 클러스터링  (0) 2021.08.26
[LV4] 4단 고음  (0) 2021.08.19
[LV4] 호텔 방 배정  (0) 2021.08.13
[LV2] 거리두기 확인하기  (0) 2021.07.29

1. 문제

1) 문제 링크

    : https://programmers.co.kr/learn/courses/30/lessons/64063

 

코딩테스트 연습 - 호텔 방 배정

 

programmers.co.kr

 

2) 설명

   사용자들이 원하는 방의 배열이 주어지고 순서대로 사용자들이 원하는 방에 배정한다.

이미 방이 사용 중이라면 원하는 방보다 높은 숫자의 빈 방 중 가장 낮은 빈 방을 배정받는다.

배정받은 방의 번호들의 배열을 리턴한다.

2. 풀이

1) 알고리즘

    : Solution 1 : set 사용

      Solution 2 : map, find 사용

 

2) 풀이 설명

  풀이 1. set 사용

일단 데이터 타입들이 long long이어서 효율성 문제가 있겠거니 생각했다.

효율성은 나중에 생각하고 일단 풀어보자 생각해서 set을 사용해서 풀어봤다.

 

1. get_allocated_room 함수를 통해 방을 배정받는다.

2. 원하는 방이 이미 배정된 방이라면 다음 방(target_room + 1)을 재귀적으로 확인한다.

3. 배정받는 방은 모두 room_set에 넣어준다.

 

알고리즘은 맞지만 예상대로 효율성을 통과하지 못했다.

왜냐하면 탐색할 때마다 room_set을 탐색해야 하는데 이 탐색이 원하는 방 + 1씩 증가하면서 계속 탐색하기 때문이다.

만약 1~1000,000,000 번 방이 다 꽉 차있고 다음 사람이 1번방을 원한다면 1~1000,000,000번 방을 set에서 찾아봐야 하기 때문이다...

그래서 다른 방법을 생각해봤는데 딱히 안 떠올라서 다른 풀이들을 약간 참고했다.

 

   풀이 2. map, find 사용

set을 사용할 때와 엄청나게 다른 풀이는 아니다. 다만 union-find 알고리즘 일부가 추가되었을 뿐이다.

room_map에는 key는 방 번호이고 value는 (비어있다면 0, 이미 배정된 방이라면 다음에 배정될 수 있는 방 번호) 이 된다.

그래서 탐색할 때마다 이 맵을 업데이트 해준다.

예를 들어 room_map 상황이 다음과 같다고 가정해본다.

{

    1 : 4,

    2 : 4,

    3 : 4,

}

이 상황에서 다음 사람이 2번 방을 원한다면 get_allocated_room 함수에서는

room_map[2]은 0이 아니므로 배정된 방이구나. -> 4번 방으로 가라네? 하고 get_allocated_room(4)를 호출한다.

그리고  room_map[4]는 0이니까 배정된 거네. 하고 4번 방을 리턴한다.

그러면 4번 방은 배정이 된 거니까 그 다음 방인 5번 방이 room_map[4]에 배정된다.

그래서 room_map은 다음과 같이 바뀐다.

{

    1 : 4

    2 : 4

    3 : 4

    4 : 5

}

그리고 다음에 2번방을 원하는 사람이 왔을 때 2 -> 4 -> 5의 탐색 과정을 다시 거치지 않도록 room_map[2]도 5로 업데이트 해준다. (어차피 get_allocated_room 함수는 재귀적으로 돌아가고 배정하는 건 그냥 한 줄 추가하는 거라 시간복잡도에 큰 영향은 없다. 즉 탐색 과정을 줄여서 얻는 속도 절감분이 room_map 업데이트를 위해 한 줄 추가해서 얻는 증가분보다 훨씬 크다)

그러면 room_map은 다시 다음과 같이 바뀐다.

{

    1 : 4,

    2 : 5,

    3 : 4,

    4 : 5

}

물론 1번이나 3번 방을 원하는 사람이 오면 1 -> 4 -> 5 나 3 -> 4 -> 5의 과정을 더 거쳐야하지만 2번 방에서만큼은 확실히 다음 탐색 때 속도를 줄여줄 수 있다.

또한 room_map이 다음 상황일 때는 

{

    1 : 2,

    2 : 3,

    3 : 4,

    4 : 5,

    ... (5 ~ 999999)

    1000000 : 0

}

3번 방을 원하는 사람이 왔을 때 3 -> 4 -> ... -> 1000000까지 탐색해야하지만 중간에 4번 방을 찾는 사람이 있어서 room_map[4] 값을 업데이트 해줬었다면, 즉 room_map이 다음과 같은 상황이라면

{

    1 : 2,

    2 : 3,

    3 : 4,

    4 : 1000000,

    ... (5 ~ 999999)

    1000000 : 0

}

3 -> 4 -> 1000000 으로 훨씬 단축시킬 수 있다.

 

이렇게 함수를 재귀적으로 돌려서 효율성 테스트까지 통과시킬 수 있었다.

3. 코드

더보기
//SOLUTION 1 : SET 사용
#include <vector>
#include <set>

using namespace std;

set<int> room_set;

long long get_allocated_room(long long target_room){
    if(room_set.find(target_room) == room_set.end()){
        return target_room;
    } else {
        return get_allocated_room(target_room + 1);
    }
}

vector<long long> solution(long long k, vector<long long> room_number) {
    vector<long long> answer;
    for(int i=0;i<room_number.size();i++){
        long long allocated_room = get_allocated_room(room_number[i]);
        answer.push_back(allocated_room);
        room_set.insert(allocated_room);
    }
    return answer;
}
//SOLUTION 2 : MAP, FIND 사용

#include <vector>
#include <map>

using namespace std;

map<long long, long long> room_map;

long long get_allocated_room(long long target_room){
    if(room_map[target_room] == 0) {
        return target_room;
    } else {
        room_map[target_room] = get_allocated_room(room_map[target_room]);
        return room_map[target_room];
    }
}

vector<long long> solution(long long k, vector<long long> room_number) {
    vector<long long> answer;
    for(int i=0;i<room_number.size();i++){
		long long allocated_room = get_allocated_room(room_number[i]);
 		answer.push_back(allocated_room);
		room_map[allocated_room] = allocated_room + 1;
    }
    return answer;
}

 

4. 느낀점

union-find를 잘 안 다뤄봐서 생각해내지 못했던 것 같다.

관련 문제들을 좀 더 풀어보자

'알고리즘 > 프로그래머스' 카테고리의 다른 글

[LV2] 뉴스 클러스터링  (0) 2021.08.26
[LV4] 4단 고음  (0) 2021.08.19
[LV3] 불량 사용자  (0) 2021.08.13
[LV2] 거리두기 확인하기  (0) 2021.07.29

1. 문제

1) 문제 링크

    : https://programmers.co.kr/learn/courses/30/lessons/81302

 

코딩테스트 연습 - 거리두기 확인하기

[["POOOP", "OXXOX", "OPXPX", "OOXOX", "POXXP"], ["POOPX", "OXPXP", "PXXXO", "OXXXO", "OOOPP"], ["PXOPX", "OXOXP", "OXPOX", "OXXOP", "PXPOX"], ["OOOXX", "XOOOX", "OOOXX", "OXOOX", "OOOOO"], ["PXPXP", "XPXPX", "PXPXP", "XPXPX", "PXPXP"]] [1, 0, 1, 1, 1]

programmers.co.kr

 

2) 설명

면접장(5 by 5)에서 면접자들이 맨해튼거리(가로 길이 + 세로 길이) 기준 2 이상 떨어져 앉아있을 때 거리두기를 지킨 것.

맨해튼거리가 2이하여도 그 사이에 파티션이 있다면 거리두기를 지킨 거다

places(면접장 배열)이 주어지면 각 place(면접장)이 거리두기를 지켰는지(1) 안 지켰는지(0)를 배열로 리턴하는 문제

참고  

- 면접자 : P

- 파티션 : X

- 빈자리 : O

2. 풀이

1) 알고리즘

    : 시뮬레이션(BFS)

 

2) 풀이 설명

- 첫 시도!

BFS를 사용해 풀이를 시도했다.

isFar 함수를 통해 각 place(면접장)이 거리두기가 지켜졌는지 확인한다.

P의 좌표들을 모두 큐에 삽입한다

큐가 빌 때까지 c(거리)가 2 이하이고 해당 P에서 방문한 적이 없는(visited[nx][ny]가 v(탐색 시작한 P의 idx)와 다른) 좌표들의 visited값을 v로 업데이트해준다

다음의 경우 visited[2, 2] 에서 1, 2가 다르기 때문에 그냥 2로 업데이트해주면 되는 거다

    visited 배열    
1 1 1   2
1 P(idx=1) 1 2 2
1 X 1 2 2 P(idx=2)
X X   2 2
P(idx=3) 3 3   2

 

만약 다음과 같이 P를 발견하면 return false 해준다

    visited 배열    
1 1 1   2
1 P(idx=1) 1 2 2
1 X 2 2 P(idx=2)
X X   2 2
        P(idx=3)

 

 

사실 P가 있냐 없냐만 보면 되는데 왜 visited 배열을 썼는가 하면 같은 자리를 두 번 이상 방문하지 않도록 하기 위해서다. 예를 들어 [0, 0]은 P(idx=1)이 좌 -> 상 이동, 상 -> 좌 이동 두 번 방문해야하는데 이때 visited배열을 보고 visited[0][0](=1) 과 v(=1)이 같기 때문에 더 방문하지 않도록 하는 것이다.

 

풀고나서 4개짜리 tuple을 썼다는 게 마음에 안 들었는데 찾아보니 더 간단한 방법이 있었다!

 

- 두 번째 시도!

사실 두 번째 풀이의 핵심은 한 면접자당 BFS를 1 depth까지만 탐색하는 거였다.

i번째 면접자가 방문한 자리를 다른 면접자가 방문한다면 이건 올바르지 않은 거리두기를 하게 된 것이다

설명하자면 교점을 기준으로 보았을 때 [x, y] 교점으로부터 거리가 1인 면접자가 둘 이상 있다면 그 면접자들 간의 거리는 2 이하가 되기 때문이다

예를 들어 다음과 같은 경우에 [4, 3]자리를 P(idx=2)가 방문했는데 P(idx=3)이 또 방문한다면 이 둘은 거리가 2 이하기 때문에 return false 해주면 된다

 

    visited 배열    
  1      
1 P(idx=1) 1   2
  X   2 P(idx=2)
        2 3
        P(idx=3)

 

 

3. 코드

더보기

첫 번째 방법

//BFS 탐색을 2 depth까지 하는 비효율적인 방식의 첫 시도
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <tuple>

using namespace std;

bool isFar(const int boardSize, vector<string> place, vector<vector<int>> &visited){
    const vector<int> dx = {-1, 1, 0, 0};
    const vector<int> dy = {0, 0, -1, 1};
    
    queue<tuple<int, int, int, int>> q;
        
    for(int i=0;i<boardSize;i++){
        for(int j=0;j<boardSize;j++){
            if(place[i][j] == 'P')
				q.push({i, j, 0, q.size()});
        }
    }
	while(!q.empty()){
      	int x = get<0>(q.front());
        int y = get<1>(q.front());
        int c = get<2>(q.front()); // 거리
        int v = get<3>(q.front()); // 탐색 중인 면접자의 index
        q.pop();
        
        visited[x][y] = v;
        
        for(int d=0;d<4;d++){
            int nx = x + dx[d];
            int ny = y + dy[d];
            int nc = c + 1;
                
            if(nx<0 || nx>=boardSize || ny<0 || ny>=boardSize
            	|| visited[nx][ny]==visited[x][y] // 탐색 중인 면접자가 방문한 적이 있다면 continue
               	|| place[nx][ny]=='X' // 파티션이면 continue
              	|| nc>2) // 다음에 갈 자리가 맨해튼 거리 2 초과면 continue
            	continue;
            
            if(place[nx][ny] == 'P')
                return false;
            q.push({nx, ny, nc, v});
        }
    }
    
    return true;
}

vector<int> solution(vector<vector<string>> places) {
	vector<int> answer;
    const int boardSize = 5;
	vector<vector<int>> visited(boardSize, vector<int>(boardSize, -1));
	
    for(auto place : places)
		answer.push_back(isFar(boardSize, place, visited));
    
	return answer;
}

 

두 번째 방법

//1 depth BFS 탐색을 통한 효율적인 방법
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <tuple>

using namespace std;

bool isFar(const int boardSize, vector<string> place){
    const vector<int> di = {-1, 1, 0, 0};
    const vector<int> dj = {0, 0, -1, 1};
    
    vector<vector<bool>> visited(boardSize, vector<bool>(boardSize, false));
        
    for(int i=0;i<boardSize;i++){
        for(int j=0;j<boardSize;j++){
            if(place[i][j] == 'P'){
                visited[i][j] = true;
            	for(int d=0;d<4;d++){
                    int ni = i + di[d];
            		int nj = j + dj[d];
                    if(ni<0 || ni>=boardSize || nj<0 || nj>=boardSize || place[ni][nj]=='X')
                        continue;
                    if(visited[ni][nj])
                        return false;
                    visited[ni][nj] = true;
                }    
            }
        }
    }
    
    return true;
}

vector<int> solution(vector<vector<string>> places) {
	vector<int> answer;
    const int boardSize = 5;
	
    for(auto place : places)
		answer.push_back(isFar(boardSize, place));
    
	return answer;
}

4. 느낀점

내 생각에는

엄청 길지는 않은 시간에

엄청 느리지는 않은 알고리즘으로

엄청 가독성 떨어지지는 않는 코드로

성공은 했지만 다 최선의 방법은 아니었다ㅠㅠ

면접자가 중심이라 생각해서 면접자를 기준으로만 생각했는데 교점을 기준으로 생각했으면 좋았을 텐데 아쉬웠다...

보이지 않는 걸 보는 연습이 필요한 것 같다..!

'알고리즘 > 프로그래머스' 카테고리의 다른 글

[LV2] 뉴스 클러스터링  (0) 2021.08.26
[LV4] 4단 고음  (0) 2021.08.19
[LV3] 불량 사용자  (0) 2021.08.13
[LV4] 호텔 방 배정  (0) 2021.08.13

+ Recent posts