728x90
반응형

1. 문제

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

 

1822번: 차집합

첫째 줄에는 집합 A의 원소의 개수 n(A)와 집합 B의 원소의 개수 n(B)가 빈 칸을 사이에 두고 주어진다. (1 ≤ n(A), n(B) ≤ 500,000)이 주어진다. 둘째 줄에는 집합 A의 원소가, 셋째 줄에는 집합 B의 원소

www.acmicpc.net

집합 A에는 속하지만, B에는 속하지 않는 원소들의 개수와 원소들을 모두 출력하는 문제이다.

집합에서 A - B (차집합)의 개념이 등장한다.

2. 풀이 방법

배열이나 리스트 등을 사용하면 해당 자료형 안에 특정 원소가 있는지 확인하려면 O(1)의 시간이 걸리기에,

접근하는데 시간이 O(1)이 걸리는 맵 자료형을 이용하여 문제를 해결했다.

 

먼저 A와 B 맵 두 개를 만들고 key 자리에 원소를 입력, value 자리에 1을 저장한다. (c++에서는 map에 key가 없어도 A[key] 형식으로 접근하면 value는 자동으로 0이 할당된다(int, int일시)

그렇게 A와 B에 각각 원소들을 저장하고,

 

iterator을 이용해

map<int, int>::iterator iter;

A의 map에 있는 모든 원소들을 돌면서 B에 있는지 확인한다.

확인하는 법은, B[iter->first] 값이 1인지 확인하면 된다. (find() 메소드를 이용했어도 가능했을 것이다; 앞에서도 얘기했듯이 map에서 key값이 없다면 0으로 value가 초기화됨)

 

 

지금 생각해보면 map이 아니라 set, 이진 트리/정렬 후 이진 탐색 등을 이용해 문제를 해결했어도 O(nlogn) 안에 문제를 해결할 수 있었을 것 같다. (set은 O(n))

 

+) 정렬과 이진 탐색을 이용해도 풀린다.

3. 배운 점

한 원소가 어떤 집합에 있는지 확인하려면 

맵 : O(1)

이진트리/이진탐색 : O(logn)

웬만하면 이 두 가지 방법을 먼저 떠올리자.

4. 코드

// 맵을 이용한 코드

 

// 정렬과 이진 탐색을 이용한 코드

 

728x90
반응형
LIST
728x90
반응형

1. 문제

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

 

11053번: 가장 긴 증가하는 부분 수열

수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이

www.acmicpc.net

길이가 n일 수열이 주어질 때, 이 수열에서 가장 긴 증가하는 부분 수열의 길이(연속일 필요는 없지만 증가해야 함)를 구하면 되는 문제이다.

2. 풀이 방법

LIS 문제의 해결법은 이 블로그에 잘 나와 있다.

https://seungkwan.tistory.com/8

 

가장 긴 증가하는 부분 수열 (Longest Increasing Subsequence)

어떠한 수열에서 가장 긴 증가하는 부분 수열(Longest Increasing Subsequence)를 찾는 문제는 꽤나 유명한 문제입니다. 다이나믹 프로그래밍을 공부할 때 예시로 자주 나오는 문제이고, 프로그래밍 대회

seungkwan.tistory.com

조금만 설명을 하자면,

기존의 DP 방법으로 풀이를 하면, 각 나오는 숫자마다 이전의 모든 증가수열에 들어가는지 여부를 확인해야 해서 O(N^2)이 걸린다.

LIS는 이진 탐색을 이용해 이 확인 과정을 O(n)에서 O(logn)으로 줄여 총 O(nlogn)만에 문제를 해결할 수 있게 했다.

 

먼저 새로운 배열 L을 만든다.

이 L에는 원소들이 정렬되어 있어 이진 탐색이 가능하다.

L의 각 자리에는 해당 길이를 가지는 증가 수열의 마지막 원소(해당 수열에서 가장 큰 수) 중 가장 작은 원소가 저장된다.

 

그리고 lower bound라는 개념이 새로 나오는데, lower bound란 배열에서 자기 자신보다 큰 최소 숫자를 말한다.

LIS를 찾는 알고리즘에서는 이진 탐색을 통해 lower bound를 찾는다.

 

1. 처음에 L에 배열의 첫 원소를 넣는다.

2. 반복문을 돌면서 L의 맨 마지막 요소와 arr[i]를 비교한다. 만약 arr[i]가L의 마지막 요소보다 크다면, 이는 이제까지 나온 가장 긴 증가수열의 가장 큰 요소보다 arr[i]가 더 크다는 뜻이므로, arr[i]를 LIS의 맨 뒤에 추가하면 LIS의 길이를 하나 더 늘릴 수 있다는 뜻이다.

따라서 L의 마지막에 arr[i]를 추가한다.

3. 만약 L의 마지막 원소 > arr[i]라면 L의 원소중 arr[i]의 lower bound(L[j]라고 하자)를 찾고, 이를 arr[i]로 교체한다. 이를 찾는 이유는 L에서 arr[i]의 lower bound 이전의 수(L[j - 1])는 arr[i]를 마지막으로 가지는 증가 수열이다. 그런데 여기에 arr[i]를 추가해 길이가 j가 된다면, j의 길이를 가지는 증가수열의 마지막 수 중 가장 작은 수는 arr[i]가 되기 때문이다.

 

이를 반복해 주면 결국 L의 길이가 수열에서 LIS의 길이가 된다.

3. 배운 점

탐색 -> 이진탐색 주로 사용

이진 탐색하려면 -> 정렬 내지 정렬된 배열 필요 (정렬을 할 필요가 없을 수도 있다는 뜻)

4. 코드

728x90
반응형
LIST
728x90
반응형

싱싱한 배추~~

1. 문제

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

 

1012번: 유기농 배추

차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에 

www.acmicpc.net

 

문제에서는 밭의 가로, 세로 크기와 심어진 배추의 개수, 각 배추의 위치가 주어진다.

이때 배추들을 건강하게 키우기 위한 최소 배추흰지렁이 개수를 구하면 되는 문제이다.

2. 해결 아이디어

dfs, bfs 등을 이용하여 쉽게 해결할 수 있는 문제이다.

다만 이번 문제를 해결하면서 작은 문제를 마주쳐 그것에 대해 기록하려고 글을 쓴다...

3. 개선할 점

코드에서 전역으로 밭(field), visited 배열을 만들고, 문제에서 설정된 최대 밭 크기가 50*50이라 SIZE를 50으로 설정했는데, 계속 오류가 났다.

https://www.acmicpc.net/board/view/99434

 

글 읽기 - 1012 유기농 지렁이) 배열 크기를 늘렸더니 계속 틀리던 게 통과 되었습니다.

댓글을 작성하려면 로그인해야 합니다.

www.acmicpc.net

그래서 질문글과 반례를 계속 찾아보던 중, 해당 글에서 배열의 크기를 51로 해서 통과되었다는 내용을 보고, 따라했더니 성공했다...

 

아니 이건 근데 억까가 아닌가...

 

앞으로 배열의 크기가 한정적인 경우(골드 이상에서)가 아니라면, 배열의 크기를 조금 널널하게 잡자.

그리고 아무 이유 없이 오류가 난다면

 

알고리즘 로직 체크 -> 변수 위치/이름 실수 -> 타입, 반복문 범위, 배열 인덱스 실수 ->.. -> 배열 크기나 변수 크기 늘려보기

순으로 시도해보자.

 

 

4. 코드

 

 

728x90
반응형
LIST
728x90
반응형

1. 문제

트리의 지름이란, 트리에서 임의의 두 점 사이의 거리 중 가장 긴 것을 말한다. 트리의 지름을 구하는 프로그램을 작성하시오.

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

 

1167번: 트리의 지름

트리가 입력으로 주어진다. 먼저 첫 번째 줄에서는 트리의 정점의 개수 V가 주어지고 (2 ≤ V ≤ 100,000)둘째 줄부터 V개의 줄에 걸쳐 간선의 정보가 다음과 같이 주어진다. 정점 번호는 1부터 V까지

www.acmicpc.net

2. 해결 아이디어

1) 각 노드마다 갈 수 있는 최대 cost를 2개까지 저장한 뒤(재귀 방식이라 시간 복잡도 O(n)), 모든 노드의 최대 cost값 2개의 합을 비교하면서 가장 큰 최대 cost의 합을 출력한다. (= 트리의 지름)

한 노드에서 갈 수 있는 가장 먼 거리 (2개)의 합은 지름이다. 따라서 최대 cost의 합은 당연히 트리의 지름이 된다.

 

트리의 왼쪽 자손과 오른쪽 자손까지의 비용을 각각 더했을 때, 그 두 합이 최대가 되면 그 두 경로는 당연히 지름이 됨을 알 수 있다.
만약 자손이 없는 경우에는 초깃값을 0으로 설정함으로써 문제를 해결할 수 있다.

 

인접 행렬 방식으로 구현하면 메모리초과가 나서 인접 리스트로 구현했다.

그래서 방문했는지를 표시하는 visited[] 배열, 가중치를 저장하는 weights[][]배열, 노드 연결 관계를 저장하는 node[][]배열, 각 노드마다 2개씩 최대 cost를 저장하는 max_depth[][] 배열 이렇게 3개의 배열이 사용되었다.

 

 

2) 정석 풀이 - dfs를 2번 돌리면 된다.

1번째에서 dfs를 통해 얻은 최대 cost를 가지는 node에 가서 한번 더 dfs를 돌려 최대 cost를 구하면 그 값이 바로 트리의 지름이다.

직관적으로는 조금 생각하기 어려웠던 풀이였던 것 같다.

 

그럼 왜 dfs를 두 번 돌리면 트리의 지름이 나올까?

dfs를 한 번 돌리면 현재 노드에서 가장 먼 노드를 찾을 수 있다.

그리고 현재 노드에서 가장 먼 그 노드까지 가는 길은 반드시 지름의 일부를 포함한다. (1번째로 긴 구간과 2번째로 긴 구간 중 적어도 1개를 포함하므로)

따라서 가장 먼 노드는 지름의 한쪽 끝에 위치하는 노드이다. 그렇기 때문에 그 노드에서 dfs를 써주면 트리의 지름을 찾을 수 있다.

3. 코드

1) 번 풀이

// Baekjoon No. 1167
// Time Complexity
// #

#include <iostream>
#include <vector>
using namespace std;

int dfs(vector<vector<int>>& node, vector<int>& visited, vector<vector<int>>& weights, vector<vector<int>>& max_depth, int nodeIdx);
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int v, v1, v2, tmp, weight, tree_length;
    int i;
    cin >> v;
    vector<int> visited(v, 0);
    // vector<vector<int>> weights(v, vector<int>(v, 0));
    vector<vector<int>> weights(v);
    vector<vector<int>> node(v);
    vector<vector<int>> max_depth(v, vector<int>(2, 0));
    for (i = 0; i < v; i++) {
        cin >> v1 >> v2 >> weight;
        v1--;
        v2--;
        // node[idx]의 2k-1번째는 nodeIdx, 2k번째는 weight
        node[v1].push_back(v2);
        weights[v1].push_back(weight);
        // node[v1].push_back(weight);
        cin >> tmp;
        while (tmp > 0) {
            v2 = tmp;
            v2--;
            cin >> weight;
            // save
            node[v1].push_back(v2);
            weights[v1].push_back(weight);
            cin >> tmp;
        }
    }

    // solve
    /** 풀이법
    1) 루트 노드를 하나 정한다
    2) 루트 노드에서부터 dfs로 각 노드의 끝까지 탐색, 만약 depth요소보다 크다면 최대 depth에 갱신한다.
    3) max_depth는 현재 노드에서 뻗을 수 있는 노드들 중 가장 긴 길이 2개를 저장한다.
    4) 각 노드에서의 max_depth 2개의 합들 중(자식 node가 1개여도 초기화가 0으로 되어 있어 상관없음)
    가장 큰 합 = 지름이므로 지름 값을 구해 출력한다.
    */
    // root node = 0
    visited[0] = 1;
    dfs(node, visited, weights, max_depth, 0);
    // output
    tree_length = 0;
    for (i = 0; i < v; i++)
        if (max_depth[i][0] + max_depth[i][1] > tree_length)
            tree_length = max_depth[i][0] + max_depth[i][1];

    cout << tree_length;
    return 0;
}

// returns the biggest depth current node can go
int dfs(vector<vector<int>>& node, vector<int>& visited, vector<vector<int>>& weights, vector<vector<int>>& max_depth, int nodeIdx){
    int size = node[nodeIdx].size(), tmp_depth = 0;
    for (int i = 0; i < size; i ++) { // node[][]
        if (!visited[node[nodeIdx][i]]) {
            visited[node[nodeIdx][i]] = 1;
            tmp_depth = dfs(node, visited, weights, max_depth, node[nodeIdx][i]);
            if (max_depth[nodeIdx][1] < tmp_depth + weights[nodeIdx][i]) {
                max_depth[nodeIdx][1] = tmp_depth + weights[nodeIdx][i];
                if (max_depth[nodeIdx][0] < max_depth[nodeIdx][1]) {
                    int tmp = max_depth[nodeIdx][0];
                    max_depth[nodeIdx][0] = max_depth[nodeIdx][1];
                    max_depth[nodeIdx][1] = tmp;
                }
            }
        }
    }
    return max_depth[nodeIdx][0];
}

 

2) 번 풀이

// Baekjoon No. 1167
// Time Complexity
// #

#include <iostream>
#include <vector>
using namespace std;

vector<int> dfs(vector<vector<int>>& node, vector<vector<int>>& weights, vector<int>& visited, int nodeIdx, int depth);
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int v, v1, v2, tmp, weight;
    int i;
    cin >> v;
    vector<int> visited(v, 0);
    vector<vector<int>> node(v);
    vector<vector<int>> weights(v);
    for (i = 0; i < v; i++) {
        cin >> v1 >> v2 >> weight >> tmp;
        v1--;
        v2--;
        node[v1].push_back(v2);
        weights[v1].push_back(weight);
        while (tmp > 0) {
            v2 = tmp;
            cin >> weight;
            cin >> tmp;
            v2--;
            node[v1].push_back(v2);
            weights[v1].push_back(weight);
        }
    }

    // solve
    visited[0] = 1;
    vector<int> node_weight = dfs(node, weights, visited, 0, 0);
    // visited init
    for (i = 0; i < v; i++)
        visited[i] = 0;
    visited[node_weight[0]] = 1;
    node_weight = dfs(node, weights, visited, node_weight[0], 0);
    
    // output
    cout << node_weight[1];
    return 0;
}
vector<int> dfs(vector<vector<int>>& node, vector<vector<int>>& weights, vector<int>& visited, int nodeIdx, int depth) {
    int size = node[nodeIdx].size();
    vector<int> node_weight = { nodeIdx, depth }, tmp = {nodeIdx, depth};
    for (int i = 0; i < size; i++) {
        if (!visited[node[nodeIdx][i]]) {
            visited[node[nodeIdx][i]] = 1;
            tmp = dfs(node, weights, visited, node[nodeIdx][i], depth + weights[nodeIdx][i]);
            if (tmp[1] > node_weight[1]) {
                node_weight[0] = tmp[0];
                node_weight[1] = tmp[1];
            }
        }
    }
    return node_weight;
}

 

4. 배운 점

1) 번 풀이에서 계속 시간초과가 나길래 분명 알고리즘은 같은데 왜 시간 초과가 나지.. 하고 고민했었다.

그런데, 함수 인자로 이차원 배열을 보내줄때 실수로 포인터가 아닌 값으로 보내줘서 생긴 문제였다.

포인터로 보내 주면 속도가 빠르다는 건 알고 있었지만, 이걸로 시간 초과가 날 줄은 몰랐다.

728x90
반응형
LIST
728x90
반응형

> 문제

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

 

3273번: 두 수의 합

n개의 서로 다른 양의 정수 a1, a2, ..., an으로 이루어진 수열이 있다. ai의 값은 1보다 크거나 같고, 1000000보다 작거나 같은 자연수이다. 자연수 x가 주어졌을 때, ai + aj = x (1 ≤ i < j ≤ n)을 만족하는

www.acmicpc.net

문제

n개의 서로 다른 양의 정수 a1, a2, ..., an으로 이루어진 수열이 있다. ai의 값은 1보다 크거나 같고, 1000000보다 작거나 같은 자연수이다. 자연수 x가 주어졌을 때, ai + aj = x (1 ≤ i < j ≤ n)을 만족하는 (ai, aj)쌍의 수를 구하는 프로그램을 작성하시오.


> 풀이

idea 1) memorization

  1. 길이가 k+1인 배열을 만들고 (memory[x+1], memory[x]에 쌍 개수 저장할 것)
  2. 입력받은 숫자들을 하나씩 순회하며 (arr[0]~ arr[n-1])
  3. 만약 memory[]의 그 숫자에 해당하는 칸이 존재한다면 (memory[arr[i]]) 1을 집어넣는다.
  4. 그리고 memory[x - arr[i]] 에 1이 담겨 있다면, 쌍이 존재한다는 의미이므로 memory[x]의 값을 1 증가시킨다.
  5. memory[x]의 값을 출력한다.

이처럼 길이가 x+1인 배열을 만들고, 두 수의 합으로 x를 만들 수 있는 쌍이 ai, aj가 다르면 유일하다는 점을 이용해 memory에 각각 x를 구성하는 ai - aj가 존재함을 저장해 ai마다 x를 만들 수 있는 aj가 있는지 확인하는 방법으로 문제를 해결할 수 있다.

// Baekjoon No. 3273 두 수의 합
// Time Complexity O(n)

#include <iostream>
#include <vector>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n, x;
    int i;
    cin >> n;
    vector<int> arr(n);
    for (i = 0; i < n; i++)
        cin >> arr[i];
    cin >> x;
    vector<int> memory(x + 1, 0);

    // solving
    for (i = 0; i < n; i++) {
        if(arr[i] < x)
            memory[arr[i]] = 1;
        if (x - arr[i] > 0 && arr[i] * 2 != x && memory[x - arr[i]])
            memory[x] ++;
    }
    cout << memory[x];

    return 0;
}

idea 2) 투 포인터

그리고 문제 유형에 적혀 있는 투 포인터 방식을 이용하는 방법이 있다.

  1. 쌍 개수를 저장할 cnt 변수를 0으로 초기화한다.
  2. 배열 arr[]에 ai 값들을 입력받는다.
  3. arr[]을 오름차순 정렬한다. (시간복잡도 O(nlogn)
  4. 정렬한 arr에서 arr[0]이 arr[n-1]이랑 더했을 때 x보다 큰지 작은지 확인한다.
  5. 만약 그 값이 x보다 크다면, arr[0] + arr[n - 1 - i] 가 x보다 작거나 같을 때까지 i를 0부터 증가시킨다. (arr의 오른쪽 idx--)
  6. 만약 그 값이 x와 같다면, cnt++, arr의 오른쪽 idx -- (또는 왼쪽 idx ++)
  7. 만약 그 값이 x보다 작다면, arr의 왼쪽 idx ++
  8. 이대로 arr의 오른쪽 idx와 왼쪽 idx가 겹칠 때까지 반복을 수행한다.

언뜻 보면 arr[l] (왼쪽 idx)를 증가시켰을 때 arr[r]과의 합이 x보다 커버리는 경우가 생길 수도 있다고 생각할 수 있지만, 애초에 arr[l]을 증가시키는 경우는 arr[l] + arr[r] < x인 경우이므로 이 경우에는 r--을 해주어야 한다.

 

> 결론

- 투 포인터를 이용하면 O(nlogn), memorization을 이용하면 O(n)

- 배열을 다룰 때 idx 범위를 조심할 것 (memorization에서 0 < arr[i] < x, 2*arr[i] != x)

 

 

728x90
반응형
LIST

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

[Baekjoon] 1992 - 쿼드트리  (1) 2022.09.21
[Baekjoon] 1019 책 페이지 - C++  (0) 2022.09.11
[Baekjoon] 2156 - 포도주 시식 / C++  (0) 2022.08.11
[Baekjoon] 21921 블로그 - C++  (0) 2022.08.11
[백준] 22943_수 / python  (0) 2022.04.05
728x90
반응형

> 문제

< 썸네일 >

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

 

2156번: 포도주 시식

효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규

www.acmicpc.net

 

 

 

2156번: 포도주 시식

효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규

www.acmicpc.net

 

 

 

2156번: 포도주 시식

효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규

www.acmicpc.net


> 풀이

1. 첫 번째 풀이 : 처음에는 이전의 계단 오르기 문제랑 동일하다고 생각해서 그때의 문제풀이를 바탕으로 arr[i]에 wines[i-1] + arr[i-3] 이랑arr[i-2]랑 더 큰 수를 더하는 방법을 사용했다.
그런데 이렇게 문제를 풀면 반례가 존재하여 문제풀이에 성공할 수 없었다.

 

 

2. 두 번째 풀이 : 질문에 있던 반례를 참고하여 1.에서 사용했던 방법은 2개의 포도주를 건너뛸때 반례에 걸린다는 것을 이해했다. 그래서 arr[i] 에 wines[i] + wines[i-1] + arr[i-3] 이랑 wines[i] + arr[i-2], wines[i] + wines[i-1] + arr[i-4]중 제일 큰 수를 더해주는 걸로 고쳤다.

 

* 그리고 arr[n-1] 이랑 arr[n-2]랑 마지막에 비교해서 출력해줘야 하는데, 그건 arr[n-2]가 최고인 경우도 존재하기 때문이다.(arr[n-1]에 1을 마지막에 붙인 경우)


> 느낀 점

같은 유형에 비슷한 조건이라고 해서 문제풀이까지 완전히 똑같지는 않다는 것을 깨닫게 되었다. 앞으로는 비슷한 유형의 문제를 보면 그 풀이를 그대로 따라하지 맣고 참고만 하여 새로운 문제풀이 방법을 만들어낸다는 생각으로 PS를 진행해야겠다.

도움을 얻었던 글

https://www.acmicpc.net/board/view/64747

 

글 읽기 - 와인을 마시지 않는 경우가 왜 필요한가요?

댓글을 작성하려면 로그인해야 합니다.

www.acmicpc.net

비슷했다고 생각했던 문제

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

 

2579번: 계단 오르기

계단 오르기 게임은 계단 아래 시작점부터 계단 꼭대기에 위치한 도착점까지 가는 게임이다. <그림 1>과 같이 각각의 계단에는 일정한 점수가 쓰여 있는데 계단을 밟으면 그 계단에 쓰여 있는 점

www.acmicpc.net


> 코드

// Baekjoon No. 2156 포도주 시식 220801~ 220807
#include <iostream>
#include <vector>
using namespace std;

int main(){
    int n, i;
    scanf("%d", &n);
    vector<int>wines(n, 0);
    vector<int>arr(n, 0);
    for(i = 0; i < n; i++)
        scanf(" %d", &wines[i]);
    
    // 초깃값만 각각 설정
    arr[0] = wines[0];
    if(n > 1)
        arr[1] = wines[0] + wines[1];
    if(n > 2)
        arr[2] = max(wines[0], wines[1]) + wines[2];
    if(n > 3)
        arr[3] = max(wines[3] + wines[2] + wines[0], wines[3] + arr[1]);
    // wines[i] + wines[i-1] + arr[i-3], wines[i] + wines[i-1] + arr[i-4], wines[i] + arr[i-2] 이렇게 3개 확인해야함.
    // 5 5 1 1 5 5 같은 반례 존재.
    for(i = 4; i < n; i++){
        arr[i] = max(max(wines[i] + wines[i-1] + arr[i-3], wines[i] + arr[i-2]), wines[i] + wines[i-1] + arr[i-4]);
    }

    if(n > 1)
        printf("%d", max(arr[n-2], arr[n-1]));
    else
        printf("%d", arr[0]);
    return 0;
}
728x90
반응형
LIST
728x90
반응형

> 문제

< 문제에 있는 사진, 어디서 많이 본 듯 하다 >

문제 링크 : 

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

 

21921번: 블로그

첫째 줄에 $X$일 동안 가장 많이 들어온 방문자 수를 출력한다. 만약 최대 방문자 수가 0명이라면 SAD를 출력한다. 만약 최대 방문자 수가 0명이 아닌 경우 둘째 줄에 기간이 몇 개 있는지 출력한다

www.acmicpc.net

문제 요약 : 총 n개의 방문자 수가 주어질 때, x일간 방문한 최대 방문자수와 그 방문자수가 있는 구간의 개수를 구하여라.


> 풀이들

  1. 첫 번째 풀이 : 구간 x 안에 있는 사람들의 합을 각각 구하고, 그걸 n - x + 1번만 반복하면 되는 줄 알았다. 하지만 시간 초과가 떠서 다른 방법을 생각해야만 했다. (시간 복잡도가 O(n-x+1) * O(x) => O(x*n)이 나와버림)
  2. 두 번째 풀이 : 구간 x 안에 있는 사람들의 합을 DP를 이용해 구하면 O(1)만에 구할 수 있다. 
    arr[0]  = arr[0], arr[1] = arr[0] + arr[1], arr[2] = arr[2] + arr[1], ... 이런 식으로 가다 보면 i번째부터 i+x-1번째 사람까지의 합은 arr[i+x-1] - arr[i-1]이다. => O(1)만에 해결 가능!

전체 코드는 다음과 같다.

https://github.com/siejwkaodj/Problem-Solve/blob/main/Baekjoon/Silver%203/21921.cpp

 

GitHub - siejwkaodj/Problem-Solve

Contribute to siejwkaodj/Problem-Solve development by creating an account on GitHub.

github.com

코드는 c++로 구현하였다.


> 느낀점

DP를 이용해 구간합을 O(1)만에 구한다는 점이 특징인 문제였다.

이전에 이 문제를 푼 것이 많은 도움이 되었다.

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

 

11660번: 구간 합 구하기 5

첫째 줄에 표의 크기 N과 합을 구해야 하는 횟수 M이 주어진다. (1 ≤ N ≤ 1024, 1 ≤ M ≤ 100,000) 둘째 줄부터 N개의 줄에는 표에 채워져 있는 수가 1행부터 차례대로 주어진다. 다음 M개의 줄에는 네

www.acmicpc.net

 

728x90
반응형
LIST

+ Recent posts