본문 바로가기
ㄴ 알고리즘/프로그래머스풀이

[프로그래머스 lv3] 광고 삽입 풀이

by 경아ㅏ 2022. 9. 19.

 프로그래머스의 모든 문제와 해설 보기[클릭]

 

해당 문제는 2021 KAKAO BLIND RECRUITMENT 출제 문제로 공식 해설이 있습니다.

2021 카카오 신입공채 1차 온라인 코딩 테스트 for Tech developers 문제해설

 

해설

1) 가장 많은 줄이 겹치는 지점이나 구간을 구할 때 떠올리면 좋은 스킬이 있다.

바로, 모든 로그의 시작점과 끝점에 대하여 (시작, +1), (끝, -1)을 저장한 배열을 만들고 해당 배열을 정렬하여 특정 포인트에 몇개의 +1과 -1이 추가 되는지 검사하는 것이다. 시간 t 지점에 겹치는 재생 목록이 n개인데 새로 시작되는 재생목록이 plus개, 끝나는 재생목록이 minus개라면 t+1 지점에는 겹치는 재생목록이 n+plus-minus 개가 된다.

 

log 배열을 이용하여 시간 t에 도달했을 때 추가되는 재생목록의 개수(재생시간의 합)을 rec[t]에 저장하자.

 

2) 각 시간에서 추가되는 재생목록의 수를 구해놓았다면, 슬라이딩 윈도우 기법을 이용해 크기가 adv_time(at)인 구간의 최대 합을 구하면 된다. 먼저, (0, at]까지의 재생시간 합(sum)을 구해놓자. 구간을 오른쪽으로 이동시킬 때는, en을 오른쪽으로 이동함과 동시에 rec[en]을 추가한다. st도 오른쪽으로 이동시키고 rec[st]를 제거한다. 

 

각 구간에서 도출된 sum 값 중에서 최댓값을 저장하여 리턴하면 정답이다.

 

 

주의 할 점

sum 값이 int 범위를 초과할 수 있으므로 자료형 long long을 사용해야 한다.

 

 

코드

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <tuple>
#include <vector>
#include <stack>
#include <queue>
#include <deque>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <iostream>

using namespace std;
using ii = pair<int, int>;
using iii = tuple<int, int, int>;
using ll = long long;

#define X first
#define Y second

ll mx;
int pt, at, ans;
int h1, m1, s1, h2, m2, s2;

vector<ii> vlog;
vector<int> rec(400000, 0);

int tosec(int h, int m, int s) {
    return h*3600+m*60+s;
}

string tostr(int sec) {

    char tmp[50];
    int l = sprintf(tmp, "%02d:%02d:%02d", sec/3600, sec%3600/60, sec%3600%60);
    return string(tmp, l);
}

void setConditions(const string& play_time, const string& adv_time, const vector<string>& logs) {

    sscanf(play_time.c_str(), "%d:%d:%d", &h1, &m1, &s1);
    pt = tosec(h1, m1, s1);

    sscanf(adv_time.c_str(), "%d:%d:%d", &h1, &m1, &s1);
    at = tosec(h1, m1, s1);

    for (auto x : logs) {
        sscanf(x.c_str(), "%d:%d:%d-%d:%d:%d", &h1, &m1, &s1, &h2, &m2, &s2);
        int st = tosec(h1, m1, s1);
        int en = tosec(h2, m2, s2);
        vlog.push_back({st, +1});
        vlog.push_back({en, -1});
    }
    sort(vlog.begin(), vlog.end());
}

//각 시간대를 포함할 때 누적되는 재생시간을 구해 rec[t]에 저장
void setRec() {

    int t = 0, idx = 0, cnt = 0;
    while (t <= pt) {
        int plus = 0, minus = 0;
        while (idx < (int)vlog.size() && vlog[idx].X == t) {
            if (vlog[idx].Y == +1) plus++;
            else minus++;
            idx++;
        }
        rec[t] = cnt;
        cnt += (plus-minus);
        t++;
    }
}

//슬라이딩 윈도우로 크기가 at인 구간의 최대 합 구하기
void findStart() {

    ll sum = 0;
    int st = 0, en = 1;

    //(0, at]까지의 합
    while (en <= at) sum += rec[en++];
    en--;
    mx = sum;

    //st와 en을 한칸씩 이동하여 합 갱신
    while (en < pt) {
        sum += rec[++en];
        sum -= rec[++st];
        if (sum > mx) { //합의 최댓값 저장
            mx = sum;
            ans = st;
        }
    }
}

string solution(string play_time, string adv_time, vector<string> logs) {

    setConditions(play_time, adv_time, logs);
    setRec();
    findStart();
    return tostr(ans);
}

 

 

댓글