[백준][C] 2096번 내려가기

2024. 11. 1. 02:30·공부하기/문제 풀기

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

 

밑으로 쭉쭉 내려가면서 어떻게 더해야 최대인지 최소인지 구하는 문제이다.

 

예제 1을 보면서 생각을 해보자

 

 

여기서의 최대값은 18로 3 + 6 + 9 의 경로로 완성된다. 최소값은 6으로 1 + 5 + 0 의 경로로 완성된다.

 

만약 최소값을 구할 때 1 -> 4 루트로 갔다면 4와 9밖에 가지 못하니 최소값을 구하지 못하게 된다.

 

이렇듯 가장 최소만 고르면 답을 찾지 못하게 되므로 많은 경우의 수를 고려해봐야한다.

 

하지만 이렇게 생각해보자.

지금까지 내려왔을 때 이게 가장 최소값임을 확인할 수 있으면 그 값에 가장 작은 값을 더해주면 최소값을 구할 수 있을 것이다.

 

예를 들어 0의 기준에서 살펴보자

0으로 이동할 수 있는 칸은 5와 6이다.

0의 입장에서 5가 6보다 작으니 최소값을 구하기 위해선 5를 더해야한다.

 

이 경우엔 정답이지만 함정에 빠질 수 있으니 올라가본다.

5에 갈 수 있는 것은 1, 2, 3이다.

그렇다면 1 -> 5 를 거쳐온 값이 가장 작게된다. (6의 경우 2, 3이 가능하니)

 

즉 칸을 한칸 씩 내려갈 때 밑의 칸은 위의 같에서 접근 가능한 칸 중 가장 작은 칸을 더해준다.

이렇게 누적합을 쌓아가면서 내려가다보면 해당 칸은 그 칸에 가능한 경로중 가장 작은 값이 된다. (시작점부터 쌓아왔다면)

 

즉, 각 칸이 그 칸에서 가능한 가장 작은 값이 된다.

 

N개의 줄이 있을 때 1부터 시작해서 정답을 쌓아가면서 재활용하는 이러한 방법을 Dynamic Programming 이라고 한다.

(동적 계획법이라고 하는데 오히려 기억하며 풀기가 더 맞는 표현이라고 한다.)

 

어떤 거대한 문제가 작은 문제의 중복으로 구성되었다면  (위의 표처럼 과정을 똑같은데 길이만 길어지는)

작은 문제를 해결하고 이 결과값을 가지고 문제를 점점 키워가면서 해결하는 것이다.

 

대표적으로 피보나치 수열이 있는데 F(n) = F(n-1) + F(n-2) 을 구할 때 F(n-1)과 F(n-2)를 각각 처음부터 구하는 것이 아닌 F(0) + F(1) 부터 시작해서 F (n-1)과 F(n-2)를 둘 다 구한 뒤 그 값으로 F(n)을 구하는 것이다.

 

이 경우 한번의 연산으로 F(n-1)과 F(n-2)를 둘다 구하기 때문에 속도가 빠르게 된다.

 

 

이제 다시 본 문제로 돌아가서

 

위에서 말한대로 누적값을 구하기 위해 N의 최대값만큼 배열을 구성해 값을 입력받고 내려가면서 누적값을 구하면 좋겠지만

 

아쉽게도 메모리가 4 MB로 크게 제한적이다. (이전 문제인 분수 합에서 메모리가 128 MB 였다.)

그래도 혹시 몰라서 구현해봤는데 바로 메모리 초과가 발생했다.

 

이 방법은 간단하게 해결했다.

문제에서 원하는 답은 가장 마지막까지 계산한 결과값으로 중간값은 필요가 없다.

그렇다면 누적합을 구하고 이전 값들은 버리면 된다.

 

그래서 배열을 3개 3개로 총 6개만 만들어서 이전, 현재만 남기고 나머지 값은 버리도록 했다.

최소값도 같은 방식으로 구현하면 된다.

 

밑은 C로 구현한 코드이다.

 

더보기

 

#include <stdio.h> 
#pragma warning(disable:4996)

int max(int a, int b)
{
	return a > b ? a : b;
}

int min(int a, int b)
{
	return a < b ? a : b;
}

int main()
{
	int map[3];
	int maxArr[2][3] = { 0 };
	int minArr[2][3] = { 0 };
	int N, maxV;

	scanf("%d", &N);

	scanf("%d %d %d", &map[0], &map[1], &map[2]);
	maxArr[1][0] = map[0];
	maxArr[1][1] = map[1];
	maxArr[1][2] = map[2];

	minArr[1][0] = map[0];
	minArr[1][1] = map[1];
	minArr[1][2] = map[2];

	for (int i = 1; i < N; i++)
	{

		for (int j = 0; j < 3; j++)
		{
			maxArr[0][j] = maxArr[1][j];
			minArr[0][j] = minArr[1][j];
		}

		scanf("%d %d %d", &map[0], &map[1], &map[2]);

		maxArr[1][0] = max(maxArr[0][0], maxArr[0][1]) + map[0];
		maxArr[1][1] = max(maxArr[0][0], max(maxArr[0][1], maxArr[0][2])) + map[1];
		maxArr[1][2] = max(maxArr[0][1], maxArr[0][2]) + map[2];

		minArr[1][0] = min(minArr[0][0], minArr[0][1]) + map[0];
		minArr[1][1] = min(minArr[0][0], min(minArr[0][1], minArr[0][2])) + map[1];
		minArr[1][2] = min(minArr[0][1], minArr[0][2]) + map[2];
	}

	printf("%d %d\n", max(maxArr[1][0], max(maxArr[1][1], maxArr[1][2])), min(minArr[1][0], min(minArr[1][1], minArr[1][2])));

	return 0;
}

 

글솜씨가 좋지 않아 이 글은 내가 다시 읽어봐도 이해가 잘 안될 것 같다.

좀 더 공부를 하면서 설명을 잘하게 되면 이 글을 다시 쓰고 싶다.

'공부하기 > 문제 풀기' 카테고리의 다른 글

[백준][C] 1021번 회전하는 큐  (0) 2024.11.09
[백준][C] 1925번 삼각형  (0) 2024.11.08
[백준][C] 2748번 피보나치 수 2  (0) 2024.11.02
[백준][C] 1735 분수의 합  (0) 2024.10.31
[백준][C] 2238 경매  (0) 2024.10.30
'공부하기/문제 풀기' 카테고리의 다른 글
  • [백준][C] 1925번 삼각형
  • [백준][C] 2748번 피보나치 수 2
  • [백준][C] 1735 분수의 합
  • [백준][C] 2238 경매
Potan7
Potan7
그냥 내 맘대로 올리는 곳 프사 : 카미쵸(kyamicho) (먼지먼지(jominji1374) 수정)
  • Potan7
    Potan의 개발 블로그
    Potan7
  • 전체
    오늘
    어제
    • 분류 전체보기 (28)
      • 공부하기 (24)
        • 문제 풀기 (15)
        • 마인크래프트 모드 만들기 (8)
        • 유니티 (1)
      • 프로젝트 (4)
        • 마인크래프트 디스플레이 애니메이션 툴 (4)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백준 경매
    백준
    마인크래프트
    트리
    분수 합
    2238번
    완전 이진 트리
    DynamicProgramming
    힙
    문제풀이
    누적합
    이진 트리
    1735
    유니티
    백분
    c
    백준 1735
    거듭제곱
    알고리즘
    dfs
    네오포지
    DP
    모드 개발
    파이썬
    티스토리챌린지
    팩토리얼
    나머지
    백준 분수 합
    오블완
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
Potan7
[백준][C] 2096번 내려가기
상단으로

티스토리툴바