GithubHelp home page GithubHelp logo

problemsolving's Introduction

Problem Solving

해결한 알고리즘 문제 정리를 위한 레포지토리입니다.

해결한 문제들

problemsolving's People

Contributors

minsoo0715 avatar

Stargazers

Park SeongJun avatar

Watchers

 avatar

problemsolving's Issues

4949: 균형잡힌 세상

4949: 균형잡힌 세상

소스 코드

아이디어

단순한 스택 괄호 검사 문제다

구현

string a;
stack<char> s = stack<char>();

while(true) {
    bool unmatched = false;
    getline(cin, a);
    if (a != ".") {
        return 0;
    }

    for (char c: a) {
        if (c == '(' || c == '[') {
            s.push(c);
        } else if (c == ']' || c == ')') {
            if (s.empty()) {
                unmatched = true;
                break;
            }

            if (c == ')' && s.top() == '(')
                s.pop();
            else if (c ==']' && s.top() == '[')
                s.pop();
            else {
                unmatched = true;
                break;
            }
        }
    }

    if (s.empty() && !unmatched) {
        cout << "yes" << endl;
    } else {
        cout << "no" << endl;
    }

    while (!s.empty()) s.pop(); // 스택 비우기
}

13305: 주유소

13305: 주유소

소스 코드

아이디어

이동하기 전에 현재 주유소를 포함해, 이전 주유소 중 리터당 가격이 싼 주유소에서 충전해서 그때 그때 비용을 계산할 때, 최소의 비용이 된다.
비용 계산이 차가 이동하는 시점이 동일하지 않지만, 이미 그곳에서 충전해서 온 것으로 가정한다.

구현

for(int i = 0; i<N-1; ++i) {
    cin >> price[i];
    min = std::min(min, price[i]);
    sum += d[i] * std::min(min, price[i]);
}

cin >> price[N-1];

cout << sum;

1931: 회의실 배정

1931: 회의실 배정

소스 코드

아이디어

회의실 배정을 할 때 많은 회의실을 배정할 수 있는 경우는 바로 끝나는 시간이 빠른 경우를 고를 때라고 생각할 수 있다. 또한 끝나는 시간이 동일하면, 더 일찍 시작할수록 유리하다.

따라서 끝나는 시간 기준, 내림차순으로 정렬하고, 끝나는 시간이 동일한 경우 시작 시간 기준으로 내림차순으로 정렬하고, 고르면 된다.

구현

sort()에 comparator 람다 표현식을 넘겨서 정렬 후, 시간이 겹치지 않는다면 그 회의를 카운팅한다.

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

using namespace std;
#define endl '\n';
vector<pair<int, int>> c = vector<pair<int, int>>(100001);

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    int N, cnt = 1, end;
    cin >> N;
    for(int i = 0; i<N; ++i) {

        cin >> c[i].first >> c[i].second;
    }

    sort(c.begin(), c.begin()+N, [](auto p1, auto p2) {
        if(p1.second == p2.second) {
            return p1.first < p2.first;
        }
        return p1.second < p2.second;
    });

    end = c[0].second;

    for(int i = 1; i<N; ++i) {
        if(end <= c[i].first) {
            ++cnt;
            end = c[i].second;
        }
    }
    cout << cnt;
    return 0;
}

14501: 퇴사

14501: 퇴사

소스 코드

아이디어

$A_n$을 n일에 상담을 진행했을 때의 최댓값으로 정의했다.
따라서 $A_i$$i + P_i\leq k \leq N$에서 최대값을 갖는 값을 $A_k$라고 할 때 이에 $P_K$를 더한 것과 같다.

$$A_i = A_k + P_k \; (A_k는 \;\; i + P_i\leq k \leq N 에서의\;최댓값)$$
또한 $i + T_i &gt; N$ 일 때 $A_i = P_k$ 이다.

$$A_i = P_i \; (i + T_i > N)$$

구현

재귀를 이용해 top-down 방식으로 구현, 편의를 위해서 $A_0$에 최대값 저장. 0일차($T_0 = 0$)는 무조건 포함시킨다고 생각하면 될 듯

int solve(int k = 0) {
    if(k + t[k] > n+1) { // 범위를 넘어가면 0
        return 0;
    }
    if(dp[k] != -1) {
        return dp[k];
    }

    int &ref = dp[k] = 0;

    for(int i = k + t[k]; i<=n; ++i) {  // k + t[k] <=  i <= n 내에서의 최대값
        ref = std::max(solve(i), ref);
    }

    return ref += p[k];
}

std::cout << solve(0);

1351: 무한 수열

1351 : 무한 수열

소스 코드

아이디어

메모이제이션 문제로 보인다.
대신 수열의 형태가 아래와 같으므로 $N$이 급격하게 줄어드는 경향이 있어 저장할 $A_i$가 띄엄띄엄 존재할 뿐만 아니라, 값의 범위가 long을 초과하기 때문에 배열로 저장하기에는 무리가 있다.
$A_i = A_{i/P} + A_{i/Q} (i ≥ 1)$

따라서 Key-Value로 저장하는 자료구조, map을 사용하면 될 것으로 보인다.

구현

std::vector대신 std::map을 통해 메모이제이션, 재귀 함수를 사용해 top-down 방식으로 구현

#include <iostream>
#include <map>

using ll = long long int;

std::map<ll, ll> dp;
ll p, q;

ll solve(ll n) {
    if(n == 0) {
        return 1;
    }
    if(dp[n] != 0) {
        return dp[n];
    }
    return dp[n] = solve(n/p) + solve(n/q);
}

1922: 네트워크 연결

1922: 네트워크 연결

소스 코드

아이디어

Greedy의 일종인 prim 알고리즘을 통해 최소 스패닝 트리를 찾는다.

구현

connected[1] = true; // 1번 노드부터 시작
while(true) {
    int min = 10001;
    int k = -1, l = -1;
    for(int i = 0; i<edges.size(); ++i) {
        pair<int, int> e = edges[i];
        if(!connected[e.first] && !connected[e.second]) continue; // 두 노드 중 연결된 노드가 하나도 없는 경우
        if(connected[e.first] && connected[e.second]) continue; // 두 노드가 이미 최소 스패닝 트리에 포함되어 있는 경우 (루프 발생 가능)
        if(costs[i] < min) { // 최소값 업데이트
            min = costs[i];
            k = e.first;
            l = e.second;
        }
    }

    if(min == 10001) break; // 아무 노드도 선택되지 않은 경우 = 모든 노드가 연결된 경우
    ans += min;
    connected[k] = true;
    connected[l] = true;
}

1541: 잃어버린 괄호

1541: 잃어버린 괄호

소스 코드

아이디어

마이너스를 만나면 다음 마이너스를 만날 때까지 괄호를 씌워 덧셈 값을 뺄셈 값으로 바꾸게 함

ex. 40-30+40-20+40 -> 40-(30+40)-(20+40) -> 최소

구현

편의상 뺄셈은 양수와 음수의 합으로 보고, 다음 음수를 만날 때까지 양수를 음수로 바꿈
예시: 40-30+40 -> 40+(-30) + (-40)

string str;
int chk = 0; // 이전 부호의 위치를 저장하고 있음.
int sum = 0;
bool ck = false; // 음수를 만났는지를 체크

for(int i = 0; i<str.length(); ++i) {
    if(str[i] == '-' || str[i] == '+') { // 부호를 만나면 이전 번호 추출
        int num = stoi(str.substr(chk, i));

        if(num < 0) { // 음수를 만나면 ck = true로 set
            ck = true;
        }else if(ck) { // 음수를 만났으면 양수를 음수로 만듦.
            num = -num;
        }
        sum += num; // 가산
        chk = i; // 부호 위치 갱신
    }
}

2839: 설탕 배달 (greedy 풀이)

2839: 설탕 배달

소스 코드

아이디어

이 문제는 5kg, 3kg 봉지만 존재하므로, 남김없이 가져가는 경우 중에서, 5kg 봉지가 최대가 될 때 봉지의 개수는 최솟값을 가짐.
따라서 설탕을 최대로 가져갈 수 있는 5kg 봉지의 개수 구해서 이 값을 하나씩 줄여나가면서, 3kg 봉지로 남김없이 가져갈 수 있을 때, 봉지의 개수가 정답임.

구현

단순히 인덱스iN/5를 초기값으로 설정하고(최대한의 5kg 봉지 갯수)
N - i * 5가 3으로 나누어떨어질 때 까지 i를 줄여나가면서 설탕봉지의 최소값을 구할 수 있음.
만약 이 값을 찾지 못하면 -1 반환.

for(int i = N/5; i>=0; --i) {
    if((N - i*5) % 3 == 0) {
        cout << (N - i * 5) / 3 + i;
        return 0;
    }
}
cout << -1;

11660: 구간 합 구하기 5

11660: 구간 합 구하기 5

소스 코드

아이디어

0,0 ~ i, j 까지의 누적합은 다음과 같다.
s[i][j] = a[i][j] + s[i-1][j] + s[i][j-1] - s[i-1][j-1]

이때 (x1, y1) ~ (x2, y2) 사이의 구간 합은 다음과 같다.
s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]

구현

입력과 동시에 값을 누적.

for(int i = 1; i<=N; ++i) {
    for(int j = 1; j<=N; ++j) {
        cin >> s[i][j];
        s[i][j] += s[i-1][j] + s[i][j-1] - s[i-1][j-1];
    }
}

for(int m = 0; m<M; ++m) {
    int x1, y1, x2, y2;
    cin >> x1 >> y1 >> x2 >> y2;
    cout << s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1] << endl;
}

2630: 색종이 만들기

2630: 색종이 만들기

소스 코드

아이디어

색종이의 개수를 일단 $1 \times 1$ 색종이의 개수로 초기화하고, 합쳐서 하나의 $n \times n$색종이로 만들 수 있는 경우 4개의 색종이를 1개의 색종이로 변경한다.
분할 정복을 통해서 $n \times n$의 색종이를 $\frac{n}{2} \times \frac{n}{2}$ 색종이 4개로 본다.

구현

vector<int> cnt = vector<int>(2); // cnt[0] : 흰색 종이의 개수, cnt[1] : 파란색 종이의 개수

int cut(int N, int x = 0, int y = 0) {
    int sum = 0; // 파란색 종이의 개수 4-sum은 흰색 종이의 개수
    bool chk = true;

    if(N == 1) {
        ++cnt[box[x][y]];
        return box[x][y];
    }
    for(int _x = x; _x <= N/2+x; _x += N/2) {
        for(int _y = y; _y <= N/2+y; _y += N/2) {
            int tmp = cut(N/2, _x, _y);
            if(tmp >= 0) sum += tmp; // 하나의 색종이가 될 수 없는 경우
            else chk = false;
        }
    }

    if(chk && (sum == 0 || sum == 4)) {
        cnt[sum / 4] -= 3; 흰색 종이 또는 파란색 종이의 개수를 3개 줄인다. (4개 -> 1개)
        return sum / 4;
    }else {
        return -1;
    }

}

1107: 리모컨

1107 : 리모컨

소스 코드

아이디어

채널을 한 채널씩 증가, 감소 및 특정 채널로 이동이라는 세 가지 액션이 가능
가능한 가까운 채널로 이동 -> +- 버튼을 이용해 목적 채널로 이동할 수 있음.

  • $(가까운 \, 채널의 \,자릿수) + |target - 가까운 \, 채널 \, 번호|$
  • $target + 1$ (0 버튼이 고장나지 않은 경우, 0번으로 이동 후 +- 버튼만 사용)
  • $|target - 100|$ (+-버튼만 사용)
    위 세 경우 중 최소값

이때 세 경우에 대해서는 무조건 최솟값을 가지므로 O(1)으로 구할 수 있음.

  1. 모든 버튼을 누를 수 있는 경우: $min\{|target-100|,\, length(target)\}$
    • 모든 버튼을 누를 수 있는 경우에는 해당 채널로 바로 이동하거나, +- 버튼을 눌러 이동하는 경우에서 무조건 최소(정답)
  2. 모든 숫자 버튼을 누를 수 없는 경우: $|target - 100|$
    • 다른 경우는 존재하지 않기 때문에 무조건 최소(정답)
  3. 목적 채널 번호가 100인 경우 : $0$
    • 0이므로 무조건 최소(정답)
  • target 번호가 $10^n$의 자릿수를 가지고 있다고 할 때 앞 부분을 0으로 채워서 $10^n$보다 작은 자릿수를 가질 수 있기 때문에 이 경우에 대한 처리도 필요함.

구현

void find(int k, int v, int e = 1)
k: 자릿수 (일의 자리의 경우 0, 십의 자리의 경우 1, ...)
v: 현재 까지의 값
e: 현재 자리 번호에 곱해지는 값 (일의 자리의 경우 1, 십의 자리의 경우 10, $n$번째 자리의 경우 $10^n$

int input;       // 입력 받은 숫자 
int len;         // 숫자의 길이
int max = -1;    // 최소값 전역 변수로 관리

void find(int k, int v, int e = 1) { // 입력 받은 숫자와 가장 가까운 수를 구함
    if(len == k)  {
        // 최소값 갱신
        return;
    }

    if(vec[0]) { // 0버튼이 사용가능하지 않은 경우, 위의 모든 자릿수를 0으로 채운 경우는 가능함.
        find(len, v);
    }
    // 다음 자리를 골라서 올림
    for(int i = 0; i<10; ++i) {
        if(vec[i]) continue;
        find(k+1, v + i * e * 10, e * 10);
    }
}

int main() {
    if(input == 100) { // 이미 그 채널인 경우
         // 0 출력
        return 0;
    }

    if(N == 10) { // 숫자 버튼이 모두 없는 경우 
        // abs(100-input) 출력
        return 0;
    }

    // size_t length() 및 std::string to_string(int)를 통해 길이 구하기
    int len = std::to_string(input).length(); 

    if(N == 0) { // 모든 숫자를 다 쓸 수 있는 경우
        // abs(input-100), len중 최소값 출력
        return 0;
    }

    // 초기 호출, 1의 자리를 고름.
    for(int i = 0; i<10; ++i) {
        if(vec[i]) continue;
        find(0, i);
    }

    return 0;
}

9095: 1, 2, 3 더하기

9095: 1, 2, 3 더하기

소스 코드

아이디어

1을 먼저 고르는 경우, 2를 먼저 고르는 경우, 3을 먼저 고르는 경우를 생각해보면

  • 1을 먼저 고르는 경우의 수 = n-1을 만드는 경우의 수
  • 2를 먼저 고르는 경우의 수 = n-2를 만드는 경우의 수
  • 3을 먼저 고르는 경우의 수 = n-3을 만드는 경우의 수

세 가지 경우를 생각해볼 수 있다.
일단 1, 2, 3에 대해서 생각해보자
1은 1의 한 가지 경우
2는 1+1, 2의 두 가지 경우
3은 1+1+1, 1+2, 2+1, 3의 세가지 경우가 존재한다.

이제 4를 생각해보자

  • 1을 먼저 고르는 경우 = 3을 만드는 경우의 수 = 3가지
  • 2를 먼저 고르는 경우 = 2를 만드는 경우의 수 = 2가지
  • 3을 먼저 고르는 경우 = 1을 만드는 경우의 수 = 1가지
    각각의 경우를 다 더해주면 7가지임을 알 수 있다.

따라서 점화식은 다음과 같음을 알 수 있다.
$$A_n = A_{n-1} + A_{n-2} + A_{n-3} \quad (n\geq4)$$

구현

입력을 여러 개 받기 때문에 재귀로 top-down 방식으로 구현 했다.

int solve(int k) {
    if(dp[k] != 0) // 값을 한번 구한 경우
       return dp[k];
    return dp[k] = solve(k-1) + solve(k-2) + solve(k-3);
}

11725: 트리의 부모 찾기

11725: 트리의 부모 찾기

소스 코드

아이디어

하위 노드들이 루트 노드 밑에 어떻게 생길지 모르기 때문에, 루트 노드로부터 노드 탐색이 필요하다.
각 노드들의 연결된 노드를 배열에 기록해놓고, 방문한 노드를 제외하고 탐색을 한다.
이때 방문하기전에 현재 노드를 정답으로 기록한다.

구현

연결된 노드가 기록된 배열을 순회하고, 방문하지 않은 노드라면 하위 노드에 대한 부모 노드를 저장 후, 재귀 호출을 통해 모든 노드를 방문한다.

void solve(int num = 1) {
    visited[num] = true;

    for(int i : v[num]) {
        if(!visited[i]) {
            ans[i] = num;
            solve(i);
        }
    }
}

1152: 단어의 개수

1152: 단어의 개수

소스 코드

아이디어

단어의 개수는 공백의 개수로 처리 가능

  • 앞, 뒤 공백이 있는 경우 : $(단어의\;개수) = (공백의\;개수) - 1$
  • 앞, 뒤 중 하나만 공백이 있는 경우: $(단어의\;개수) = (공백의\;개수)$
  • 앞, 뒤 공백이 없는 경우: 단어의 개수 = $(단어의\;개수) = (공백의\;개수) + 1$

구현

공백의 개수를 구하고, 앞 뒤 공백 여부에 따라서 답 계산

int cnt = 0, len = s.length();

for(int i = 0; i<len; ++i) {
    if(s[i] == ' ') ++cnt;
}

if(s[0] == ' ' && s[len-1] == ' ') {
    cnt += -1;
}else if(s[0] != ' ' && s[len-1] != ' ') {
    cnt += 1;
}
cout << cnt;

2884: 알람 시계

2884: 알람 시계

소스 코드

아이디어

시간:분을 분 단위로 변경해서 45분을 빼준다. 만약 이때 음수가 된다면 전날이 되는 것이므로 24*60을 더해줌.

구현

m = h*60 + m - 45;
if(m < 0) {
    m += 24*60;
}

cout << m / 60 << ' ' << m % 60;

16139: 인간-컴퓨터 상호작용

16139: 인간-컴퓨터 상호작용

소스 코드

아이디어

26개의 문자에 대해서 누적합 배열을 만들고 구간의 합을 구한다 (특정 문자가 등장하면 +1)
문자 i에 대해서 구간 [x, y]의 구간합은 다음과 같다.
s[i][y] - s[i][x-1] 단, x == 0일 때 s[i][x-1] == 0이다

구현

문자가 아스키코드로 표현됨을 활용하여 (문자의 아스키코드) - 'a'로 알파벳을 0~25로 매칭한다

for(int i = 0; i<str.length(); ++i) {
    s[str[i] - 'a'][i] += 1;
    if(i > 0) {
        for(int j = 'a'; j<='z'; ++j) {
            s[j - 'a'][i] += s[j - 'a'][i-1];
        }
    }
}

for(int i = 0; i<N; ++i) {
    cin >> alphabet >> m >> n;
    if(m == 0) {
        cout << s[alphabet-'a'][n] << endl;
        continue;
    }
    cout << s[alphabet-'a'][n] - s[alphabet-'a'][m-1] << endl;
}

2480: 주사위 세계

2480: 주사위 세계

소스 코드

아이디어

if문 사용

구현

삼항연산자, if문을 통해 조건에 맞게 구현

#include <iostream>

using namespace std;
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    int a, b, c;
    cin >> a >> b >> c;

    if(a == b && b == c) {
        cout << 10000 + a * 1000;
    }else if(a == b || b == c || a == c) {
        cout << 1000 + (a == b ? a : b == c ? b : a == c ? c : 0) * 100;
    }else {
        cout << max(max(a,b), c) * 100;
    }
    return 0;
}

2193: 이친수

2193: 이친수

소스 코드

아이디어

한 자리 이친수의 개수는 1, 두 자리 이친수의 개수는 1이다.

n 자리 이친수를 만들 때, n-1자리 이친수 뒤에 0이나 1을 뒤에 붙이는 방법을 생각해보자
n-1 자리 이친수에 0을 붙이는 경우, 무조건 이친수가 나오게 된다.
n-1 자리 이친수에 1을 붙이는 경우, 앞이 0으로 끝나는 경우에만 이친수가 된다.
이때 n-2 자리 이친수의 개수를 생각해보자,
이 n-2 자리 이친수의 개수는 0으로 끝나는 n-1자리 이친수의 개수와 같다.(n-2자리 이친수에 0을 붙이면 무조건 n-1자리 이친수이므로)

따라서 n자리 이친수의 개수는 다음과 같다.
$$A_n = A_{n-1} + A_{n-2}\;(n \geq 3)$$
$$A_1 = 1\; , A_2 = 1$$

구현

위 점화식을 down-to-top 방식으로 구현

dp[1] = 1;
dp[2] = 1;

for(int i = 3; i<=n; ++i) {
    dp[i] = dp[i-1] + dp[i-2];
}

2559: 수열

2559: 수열

소스 코드

아이디어

1차원 누적합 배열을 만들면 s[2] - s[1] = v[2]이고 s[3] - s[1] = v[3] + v[2]이다.
따라서 K일 동안의 합은 s[i] - s[i-K]로 나타낼 수 있다. 이에 대해서 최댓값을 구하면 된다.

구현

누적합을 구해서 sum[i] - sum[i-K]가 최대가 되는 구간을 구한다.

for(int i = K; i <= N; ++i) {
    max = std::max(max, s[i]-s[i-K]);
}

9251: LCS

9251: LCS

소스 코드

아이디어

  • dp[i][j]를 다음과 같이 정의한다.

    str1의 i번째 까지의 부분 문자열과 str2의 i번째 까지의 부분 문자열의 LCS(길이)

  • 두 문자열 ACAYKP, CAPCAK에 대해서 살펴보자

    • ACAYCAPCA
      ACAYCAPCA의 LCS는 ACA이고,
      ACAYKCAPC의 LCS는 AC이다.
      이때 A, K로 다른 문자이므로, 두 경우 중 더 길이가 긴 것이 ACAYK CAPCA의 LCS라고 할 수 있다.

    • ACAYKCAPCAK
      ACAYCAPCA의 LCS에 K를 추가하는 경우 이므로
      ACAY, CAPCA의 LCS에 K가 추가 된 문자열이 ACAYKCAPCAK의 LCS이다.

  • 따라서 LCS의 길이는 다음과 같이 정리할 수 있다. ( string은 0-based, dp 배열은 1-based)

    • str1[i-1] == str2[j-1]일때
      dp[i][j] = dp[i-1][j-1] + 1
    • 아닐때
      dp[i][j] = max(dp[i-1][j], dp[i][j-1])
* A C A Y K P
C 0 0 0 0 0 0
A 0 0 0 0 0 0
P 0 0 0 0 0 0
C 0 0 0 0 0 0
A 0 0 0 0 0 0
K 0 0 0 0 0 0

i = 1 일 때 (string idx = 0)

* A C A Y K P
C 0 1 1 1 1 1
A 0 0 0 0 0 0
P 0 0 0 0 0 0
C 0 0 0 0 0 0
A 0 0 0 0 0 0
K 0 0 0 0 0 0

반복

* A C A Y K P
C 0 1 1 1 1 1
A 1 1 2 2 2 2
P 1 1 2 2 2 3
C 1 2 2 2 2 3
A 2 2 3 3 3 3
K 2 2 3 3 4 4

구현

for(int i = 1; i<=l1; ++i) {
    for(int j = 1; j<=l2; ++j) {
        if(str1[i-1] == str2[j-1]) {
            dp[i][j] = dp[i-1][j-1] + 1;
        }else {
            dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
        }
    }
}

11659: 구간 합 구하기 4

11659: 구간 합 구하기 4

소스 코드

아이디어

누적합 배열에서 특정 구간 [i, j]의 합은 다음과 같다.
$$S_{j} - S_{i-1} ~~~(S_{0} = 0)$$

구현

// 누적합 배열 생성
for(int i = 1; i<=N; ++i) {
    cin >> v[i];
    if(i > 1) {
        v[i] += v[i-1];
    }
}

// 구간 합 출력
for(int i = 1; i<=M; ++i) {
    int m, n;
    cin >> m >> n;
    cout << v[n] - v[m-1] << endl;
}

11047: 동전 0

11047: 동전 0

소스 코드

아이디어

동전 개수는 높은 액수의 동전을 가능한 많이 가져올 때 최소가 된다.
-> 그리디

구현

for(int i = N-1; i>=0; --i) {
    if(K < coin[i]) continue;
    if(K == 0) break;
    cnt += K / coin[i];
    K %= coin[i];
}

cout << cnt;

2839: 설탕배달 (DP 풀이)

2839: 설탕 배달

소스 코드

아이디어

설탕이 Nkg일 때 최소한의 봉지의 양을 $A_N$이라고 하면
처음에 5kg 봉지로 설탕을 넣는 경우와, 3kg 봉지로 설탕을 넣는 경우로 나눌 수 있다.
5kg 봉지로 설탕을 넣기 시작할 때의 봉지의 최솟값은 결국 $A_{N-5} + 1$와 같다. 3kg의 경우도 $A_{N-3} + 1$과 같다.
즉, 이 두 경우 중에 가능한 경우에서 최솟값을 가지는 게 $A_N$이다.

따라서 다음과 같이 식을 세울 수 있다.

  • $A_{N-5}$와, $A_{N-3}$이 가능한 경우
    $$A_N = min(A_{N-5}, A_{N-3}) + 1$$

  • 둘다 불가능한 경우 $$A_N = -1$$

  • 둘 중 하나만 불가능한 경우 $$A_N = A_{N-5} + 1\quad 또는\quad A_N = A_{N-3} + 1$$

구현

위 점화식을 반복문을 통해 down-top 방식으로 구현.

vector<int> dp = vector<int>(5001, -1); // 불가능한 경우의 값인 -1을 초깃값으로 지정

dp[3] = dp[5] = 1; // 초기 값

for(int i = 6; i<=N; ++i) {
    s5 = dp[i-5]; s3 = dp[i-3];
    if(s5 > 0 && s3 > 0) {               // 두 경우 모두 가능한 경우
        dp[i] = min(s3, s5) + 1;
    }else if(s5 < 0 && s3 < 0) {         // 두 경우 모두 불가능한 경우
        dp[i] = -1;
    } else {                             // 두 경우 중 하나만 가능한 경우
        dp[i] = (s3 > 0 ? s3 : s5) + 1;  // 가능한 경우의 수 + 1
    }
}
    cout << dp[N];
    return 0;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.