프로그래머스 - 연속 펄스 부분 수열의 합(Lv.3)

2 minute read

문제풀이1 (실패, 시간초과)

  • 펄스 수열이 적용된 각 원소는 1이 곱해지느냐 -1이 곱해지느냐로 나뉜다.
  • 수열의 첫 원소에 1이 곱해지는 경우와 -1이 곱해지는 경우 두 가지로 펄스 수열을 좁힐 수 있다.
  • 두 펄스 수열의 부분 연속 배열을 찾아서 그 합이 최대가 되는 경우를 찾으면 된다.
  • 부분합으로 접근하였고, 투 포인터를 이용하였으나 시간초과, 실패(segmentation fault)로 통과하지 못했다.

구현

#include <string>
#include <vector>
#include <iostream>

using namespace std;

// Debugging
void print_vector(vector<long long> v){
    for(auto ele:v){
        cout << ele << " ";
    }
    cout << endl;
}

void get_pulse_array(vector<int>& sequence, vector<int>& pulse1, vector<int>& pulse2){
    int n=sequence.size();
    for(int i=0; i<n-1; i+=2){
        pulse1.push_back(sequence[i]*1);
        pulse1.push_back(sequence[i+1]*(-1));
        pulse2.push_back(sequence[i]*(-1));
        pulse2.push_back(sequence[i+1]*1);
    }
}

// 부분합을 구한다. p1_sum[2] = p1_sum[0] + p1_sum[1] + p1_sum[2]
void get_partial_sum(vector<int>& pulse1, vector<int>& pulse2, vector<long long>& p1_sum, vector<long long>& p2_sum){
    int n = pulse1.size();
    int sum1 = 0, sum2 = 0;
    for(int i=0; i<n; i++){
        sum1 += pulse1[i];
        p1_sum.push_back(sum1);
        sum2 += pulse2[i];
        p2_sum.push_back(sum2);
    }
}

long long get_max_pulse(vector<long long>& p1_sum, vector<long long>& p2_sum){
    int max = p1_sum[0];
    int n = p1_sum.size();
    int start, end;
    long long val1, val2, tmp;
    
    for(int len=n; len>=1; len--){
        int diff = n-len;
        for(int i=0; i<=diff; i++){
            start = i;
            end = (n-1) - (diff - i);
            if(start>0){
                val1 = p1_sum[end] - p1_sum[start-1];
                val2 = p2_sum[end] - p2_sum[start-1];
            }
            else{
                val1 = p1_sum[end];
                val2 = p2_sum[end];
            }
            tmp = val1>val2 ? val1 : val2;
            if(max<tmp) max = tmp;
        }
    }
    return max;
}

long long solution(vector<int> sequence) {
    long long answer = 0;
    
    vector<int> pulse1, pulse2;
    get_pulse_array(sequence, pulse1, pulse2);
    
    vector<long long> p1_sum, p2_sum;
    get_partial_sum(pulse1, pulse2, p1_sum, p2_sum);
    
    answer = get_max_pulse(p1_sum, p2_sum);
    
    return answer;
}

개선한 점

  • 동적 계획법(Dynamic Programming)을 이용해 시간초과 문제를 해결할 수 있었다.
  • 연속 부분 배열을 어느 시점에서 끊어내는지가 문제 풀이의 핵심이 되는 부분이다.
  • 탐색을 하는 원소가 이전까지 탐색된 배열들과 합쳐졌을 때, 값이 더 작아진다면 해당 연속 부분 배열을 유지할 필요가 없다.
  • 즉, dp[i] = max(dp[i-1]+arr[i], arr[i]) 라는 수식을 따를 수 있다.

구현(All Pass)

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

using namespace std;

void get_pulse_array(vector<int>& sequence, vector<long long>& pulse1, vector<long long>& pulse2){
    int n=sequence.size();
    for(int i=0; i<n; i++){
        if(i%2==0){
            pulse1.push_back(sequence[i]);
            pulse2.push_back(sequence[i]*(-1));
        }
        else{
            pulse1.push_back(sequence[i]*(-1));
            pulse2.push_back(sequence[i]);
        }
    }
}

long long get_max_pulse(vector<long long>& pulse1, vector<long long>& pulse2){
    int n = pulse1.size();
    vector<long long> dp1(n+1,0), dp2(n+1,0);
    long long ret = -99999999;
    for(int i=1; i<=n; i++){
        dp1[i] = max(dp1[i-1]+(long long)pulse1[i-1], (long long)pulse1[i-1]);
        dp2[i] = max(dp2[i-1]+(long long)pulse2[i-1], (long long)pulse2[i-1]);
        ret = max(ret, dp1[i]);
        ret = max(ret, dp2[i]);
    }

    return ret;
}

long long solution(vector<int> sequence) {
    long long answer = 0;
    
    vector<long long> pulse1, pulse2;
    get_pulse_array(sequence, pulse1, pulse2);
    
    if(sequence.size()==1) // 길이가 1인 경우
        answer = max(pulse1[0], pulse2[0]);
    else
        answer = get_max_pulse(pulse1, pulse2);
    
    
    return answer;
}

Leave a comment