Computer Science
탄탄한 기반 실력을 위한
전공과 이론 지식 모음
Today I Learned!
배웠으면 기록을 해야지
TIL 사진
Flutter 사진
Flutter로 모바일까지
거꾸로캠퍼스 코딩랩 Flutter 앱개발 강사
스파르타코딩클럽 즉문즉답 튜터
카카오테크캠퍼스 3기 학습코치
프로필 사진
박성민
임베디드 세계에
발을 들인 박치기 공룡
임베디드 사진
EMBEDDED SYSTEM
임베디드 SW와 HW, 이론부터 실전까지
ALGORITHM
알고리즘 해결 전략 기록
🎓
중앙대학교 소프트웨어학부
텔레칩스 차량용 임베디드 스쿨 3기
애플 개발자 아카데미 1기
깃허브 사진
GitHub
프로젝트 모아보기
Instagram
인스타그램 사진

Develop/알고리즘

[백준] 10816 - 숫자 카드 2

sm_amoled 2021. 8. 13. 13:50

문제 링크

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

문제

숫자 카드는 정수 하나가 적혀져 있는 카드이다. 상근이는 숫자 카드 N개를 가지고 있다. 정수 M개가 주어졌을 때, 이 수가 적혀있는 숫자 카드를 상근이가 몇 개 가지고 있는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 상근이가 가지고 있는 숫자 카드의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 숫자 카드에 적혀있는 정수가 주어진다. 숫자 카드에 적혀있는 수는 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다.

셋째 줄에는 M(1 ≤ M ≤ 500,000)이 주어진다. 넷째 줄에는 상근이가 몇 개 가지고 있는 숫자 카드인지 구해야 할 M개의 정수가 주어지며, 이 수는 공백으로 구분되어져 있다. 이 수도 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다.

출력

첫째 줄에 입력으로 주어진 M개의 수에 대해서, 각 수가 적힌 숫자 카드를 상근이가 몇 개 가지고 있는지를 공백으로 구분해 출력한다.

조건

  • 시간 제한 : 1s
  • 메모리 제한 : 256MB

해설

정렬된 배열에서 특정 값 이상의 원소 중 가장 작은 index를 반환하는 lower_bound() 함수와 특정 값 초과 원소 중 가장 작은 index를 반환하는 upper_boud() 함수를 이용하여 해결하는 문제이다.

풀이

[ 정답 코드는 맨 아래에 있습니다 ]

vector로 카드의 값들을 입력받고, 카드에 적힌 숫자들은 탐색 알고리즘을 사용하기 위해 정렬해주었다.

int N;
cin >> N;
vector<int> card(N);

for(int i = 0; i < N; i++) {
	  cin >> card[i];
}

sort(card.begin(), card.end());

찾고자 하는 숫자는 입력과 동시에 처리 후 출력한다. upper_bound()함수를 사용하면 내가 원하는 값의 원소+1의 index를 얻을 수 있고, lower_bound()함수를 사용하면 내가 원하는 값의 첫 index를 얻을 수 있다. 따라서 upper_bound() - lower_bound()를 실행하면 원하는 원소의 개수를 결과로 얻을 수 있다. ( 1 2 2 2 3 일 때, index로는 4 - 1 = 3 → 3개)

int M;
cin >> M;

for(int i =0; i < M; i++) {
    int num;
    cin >> num;
    
    cout << upper_bound(card.begin(), card.end(), num) - lower_bound(card.begin(), card.end(), num) << " ";
}

upper_bound, lower_bound 함수는 인자로 탐색 시작 주소값, 탐색 끝 주소값, 찾으려는 값을 받는다.

이렇게 하면 근데 입력시간이 병목이 되어 시간 초과가 뜬다!

ios_base::sync_with_stdio(0);
cin.tie(0);

main 함수에 위 두 줄을 추가해주면 cin(), cout() 을 통한 input, output에 걸리는 시간을 줄여 코드 실행 속도를 올려준다.


고민1

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main() {
    int N, M;
    int temp;
    vector<int> card, target;
    
    cin >> N;
    for(int i = 0; i < N; i++) {
        scanf("%d", &temp);
        card.push_back(temp);
    }
    cin >> M;
    for(int i = 0; i < M; i++) {
        scanf("%d", &temp);
        target.push_back(temp);
    }
    
    sort(card.begin(), card.end());
    
    // for(auto x : card) {
    //     printf("%d ", x);
    // }
    
    int left, right, mid, minIndex, count;
    int targetNumb;
    for(int i = 0; i < M; i++) {
        left = 0; right = N-1; minIndex = N; count = 0;
        targetNumb = target.at(i);
      
        while(left <= right) {
            mid = (left+right)/2;
            // printf("-> %d %d %d\n", mid, card.at(mid),targetNumb);
            if(mid == 0) {
                if(targetNumb > card.at(mid)) {
                    left = mid + 1;
                    minIndex = left;
                } else {
                    minIndex = 0;
                    break;
                }
            }
            
            if(targetNumb == card.at(mid)) {
                while(targetNumb == card.at(mid-1)) {
                    mid--;
                    if(mid == 0) break;
                }
                minIndex=mid;
                break;
            } else if(targetNumb < card.at(mid)) {
                right = mid - 1;
            } else {
                left = mid + 1;
                minIndex = left;
            }
        }
        
        if(0 <= N && minIndex < N) {
            while(target.at(i) == card.at(minIndex)) {
                count++;
                if(++minIndex >= N) {
                    break;
                }
            }
        }
        
        cout << count << " ";
    }
    
   return 0;
}

이렇게 만들어봤는데, 시간초과가 뜨더라... 위에 있는 입출력 시간을 줄여주는 코드를 사용해보고 cin, cout으로도 바꿔봤는데, 오래걸리는 걸 보니 알고리즘 자체가 시간이 더 걸리는건가봐. ㅠㅠ 그래서 밑에 코드로 개선을 해봤다.

고민 2

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
struct tar {
    int index;
    int number;
    int count;
};

bool sortIndex (tar a, tar b) {
    return a.index < b.index;    
}

bool sortNumber (tar a, tar b) {
    return a.number < b.number;
}

int main() {
    int N, M;
    int temp;
    vector<int> card;
    vector<tar> target;
    
    cin >> N;
    for(int i = 0; i < N; i++) {
        scanf("%d", &temp);
        card.push_back(temp);
    }
    cin >> M;
    for(int i = 0; i < M; i++) {
        scanf("%d", &temp);
        target.push_back({i, temp});
    }

    sort(card.begin(), card.end());
    sort(target.begin(), target.end(), sortNumber);
    
    int left, right, mid;
    int count, indexStart, leftStart = 0;
    
    for(int i = 0; i < M; i++) {
        count = 0; indexStart = -1;
        left = leftStart; right= N-1;
        
        while(left <= right) {
            mid = (left+right)/2;
            // printf("-> %d %d %d\n", mid, card.at(mid),target.at(i).number);

            if(target.at(i).number < card.at(mid)) {
                right = mid-1;
            } else if (target.at(i).number > card.at(mid)) {
                left = mid+1;
            } else {
                left = right+1;
                indexStart = mid;
            }
        }
        
        if(indexStart == -1) {
            continue;
        }
        
        while(target.at(i).number == card.at(indexStart)) {
            if(indexStart == 0) { indexStart--; break; } 
            indexStart--; 
        }
        indexStart += 1; // indexStart에서 원하는 값 시작!
        
        while(target.at(i).number == card.at(indexStart)) {
            indexStart++;
            count++;
            if(indexStart == N) { indexStart = N-1; break;}
        }
        
        target.at(i).count = count;
    }
    
    sort(target.begin(), target.end(), sortIndex);
    
    for(int i = 0; i < M; i++) {
        printf("%d ", target.at(i).count);    
    }
    
   return 0;
}

이 친구도 역시나 시간초과가 떴다. 이 알고리즘은 맞추고자 하는 수도 정렬을 해서 이분 탐색을 하는 범위 자체를 점점 줄여나가는 컨셉이였는데, 아무래도 sort 알고리즘이 3번 나오고 했던게 패인이 아닌가 싶다. ㅠㅠㅠ

코멘트

시간 초과가 진짜 빡치는 것 같아... 알고리즘 개량해서 더 개선했는데 59%에서 시간 초과로 멈추고, 통과한 밑에있는 코드는 2%에서 시간초과로 멈추다가 입출력 병목 개선하는 코드 넣으니깐 바로 통과하더라. 내 5시간은 어디로 간건가... 알고리즘 트레이닝 책 찾아보니깐 병목 개선하는 코드가 2장에 나와있더라고... 이거 한 번 써볼걸 그랬네. 역시 사람은 당해야 기억을 하게 되는 것 같다 ㅋㅋㅋㅋㅋㅋ 시발....

ios_base::sync_with_stdio(0);
cin.tie(0);


코드

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    int N;
    cin >> N;
    vector<int> card(N);
    
    for(int i = 0; i < N; i++) {
        cin >> card[i];
    }
    
    sort(card.begin(), card.end());
    
    int M;
    cin >> M;
    
    for(int i =0; i < M; i++) {
        int num;
        cin >> num;
        
        cout << upper_bound(card.begin(), card.end(), num) - lower_bound(card.begin(), card.end(), num) << " ";
    }
    return 0;
}
320x100

'Develop > 알고리즘' 카테고리의 다른 글

[백준] 10828 - 스택  (0) 2021.08.13
[백준] 11052 - 카드 구매하기  (0) 2021.08.13
[백준] 2217 - 로프 문제  (0) 2021.08.13
[백준] 2512 - 예산  (0) 2021.08.13
[백준] 9465 - 스티커  (0) 2021.08.13