PS/PS Log

23.07.26. 풀었던 문제들 복기

hyelie 2023. 7. 26. 17:52

Leetcode 1870. Minimum Speed to Arrive on Time

 문제를 읽다 보니, binary search라는 느낌이 딱 왔다. 그리고 정리를 하다 보니, 아래와 같은 flow로 이건 무조건 binary search (parametric search)라는 생각이 들었다.

1. 불가능한 조건은 언제 나올까?
아무리 큰 speed로 dist[i]를 나누어도 올림한다. 즉, dist[i] 하나당 최소 1시간은 필요하다는 의미이다. (단, 마지막 dist[i]는 예외이다.) 따라서, hour 안에 도착하기 위해서는 n-1보다 큰 시간이 필요하다.
만약 hour <= n-1인 경우 불가능하다는 의미이다.
hour가 n-1보다 epsilon만큼 큰 경우는 speed를 매우 크게 설정해 달성할 수는 있다.

2. 가능한 조건을 어떻게 추릴까?
정답이 10^7을 초과하지 않는다. -> 최대 10^7로 잡으면 된다는 것 같다. 여기서 binary search가 맞다는 것을 확신했다.

 

첫 접근

// Runtime 473 ms Beats 14.39%
// Memory 101.5 MB Beats 67.19%

class Solution {
public:
    bool available(vector<int>& dist, int speed, double hour){
        double sum = 0;
        for(int i = 0; i<dist.size()-1; i++){
            sum += (dist[i] / speed);
            if(dist[i] % speed != 0) sum += 1;
        }
        // 마지막 element만이 hour의 소수점에 관여한다.
        int last = dist[dist.size()-1];
        sum += (double)last / speed;

        if(sum <= hour) return true;
        else return false;
    }
    int minSpeedOnTime(vector<int>& dist, double hour) {
        int n = dist.size();
        if(hour <= n-1) return -1;

        int start = 1, end = 10000000, mid;
        while(start < end){
            mid = (start + end) / 2;
            bool isAvailable = available(dist, mid, hour);
            if(isAvailable) end = mid;
            else start = mid + 1;
        }

        return end;
    }
};

 

시간복잡도

 dist length를 n이라고 했을 때, available() 함수는 모든 dist를 순회하므로 O(n)이다. 한편 binary search를 사용하는데 min값은 1, max값은 10$^7$이므로 O(10$^7$logn)이다.

 

공간복잡도

 추가로 array 형식의 공간을 사용하지 않는다. 따라서 O(1).

 

개선

 속도가 너무 느려 solution들을 보았다. 전체 flow는 동일했지만 딱 한 부분이 달랐다.

 나의 경우 availabile() 함수 내에 아래와 같이 / 연산을 하고 %를 사용해 있는 경우 +1을 했는데,

sum += (dist[i] / speed);
if(dist[i] % speed != 0) sum += 1;

 

 대부분 solution의 경우 아래와 같이 ceil() 함수를 사용했다. 이렇게 쓰니 속도가 매우 빨라졌다. 아마 내부적인 로직 차이가 있는 것 같다.

sum += ceil((double)dist[i] / speed);

// Runtime 327 ms Beats 64.60%
// Memory 101.5 MB Beats 67.19%

 

 

 

백준 25374. 등급 계산하기

 그냥 빡구현 문제... 오랜만에 손대니까 너무 오래 걸렸다. 1시간쯤,, 갈 길이 멀다.

 

 일단 풀이는, [0, 4, 7, 12, 17, 20, 17, 12, 7, 4]와 같이 각 등급별 할당될 인원을 1-indexing으로 만든다. 이후 [현재 등급]을 기록하고, 

  • 현재 등급에 넣을 수 있는 경우, 넣는다.
  • 넣을 수 없는 경우, 다음 등급을 탐색한다.
    • 단, 넣을 수 없는 경우이지만 이전 등수와 점수가 같은 경우 다음 등급에 있는 인원수를 하나 빼앗아 현재 등급에 할당한다.

 위 로직을 따라 풀었다.

void solve(){
	int N; cin>>N;
    vector<int> points(N); for(int i = 0; i<N; i++) cin>>points[i];

    sort(points.begin(), points.end(), greater<int>());

    vector<int> cuts = {0, 4, 7, 12, 17, 20, 17, 12, 7, 4}; // 차례대로 1등급 ~ 9등급에 할당될 인원. 1-indexing
    vector<int> answer(10, 0); // 1-indexing

    int grade = 1; answer[grade]++; cuts[grade]--; // *** 실수 : 1등을 넣었으면 1등급 할당 인원에서 1명 빼야 했다.
    for(int i = 1; i<N; i++){
        if(cuts[grade] > 0){ // 현재 등급에 넣을 수 있는 경우, 아무것도 하지 않고 넣으면 된다. 넣고, 할당량에서 1을 빼면 된다.
        }
        else{ // 넣을 수 없는 경우 다음 등급을 탐색한다.
            // 단, 이전 등수와 점수가 같은 경우 다음 등급에 있는 인원수를 하나 빼 와서 현재 등급에 할당해야 한다.
            if(points[i-1] == points[i]){ // 점수 같은 경우
                int nextGrade = grade + 1;
                while(cuts[nextGrade] == 0) nextGrade++; // 할당할 수 있는 등급이 나올 때까지 등급 뒤로 감.
                cuts[grade]++; // 인원수 하나 빼 옴
                cuts[nextGrade]--;
            }
            else{
                int nextGrade = grade + 1; // 점수 다른 경우 
                while(cuts[nextGrade] == 0) nextGrade++; // 할당할 수 있는 등급이 나올 때까지 등급 뒤로 감.
                grade = nextGrade;
            }
        }
        answer[grade]++;
        cuts[grade]--;
    }

    for(int i = 1; i<10; i++) cout<<answer[i]<<'\n';
}

 

 

후기

 오랜만에 푸니까 정신 나갈 것 같다. 문제도 안읽히고... 다시 익숙해져야겠지.