본문 바로가기

알고리즘/백준 문제 풀이

[Python] 1563번 개근상

728x90

 

 

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

 

1563번: 개근상

백준중학교에서는 학기가 끝날 무렵에 출결사항을 보고 개근상을 줄 것인지 말 것인지 결정한다. 이 학교는 이상해서 학생들이 학교를 너무 자주 빠지기 때문에, 개근상을 주는 조건이 조금 독

www.acmicpc.net


 

23/03/09

 

 

3차원 DP문제로, 상태를 정의하기만 하면 점화식을 구성하는 것은 그렇게 어렵지 않아서, 쉽게 풀 수 있는 문제이다.


 

문제 접근 방식:

 

 

문제를 자세히 보면, 개근상을 받을 수 없는 경우를 주목해야 할 필요가 있다.

 

개근상을 받을 수 없는 경우는 $N$일 중에서 지각이 $2$회 이상이거나, 결석이 연속 $3$회 이상이면 개근상을 받을 수 없다.

 

반대로 말하면, $N$일 중에서 지각이 $1$회 이하이거나, 결석이 연속 $2$회 이하이면 개근상을 받을 수 있다는 것이다.

 

 

점화식을 구성할 때, 영향을 미치는 요인은 몇 일 나왔는지, 그 중 지각이 몇 번인지, 지금까지 결석을 연속으로 몇 번 했는지가 영향을 미친다는 사실을 알 수 있다.

 

이 사실을 통해 $\mathrm{DP}$식을 다음과 같이 구성할 수 있다.

 

$\mathrm{DP}[n][i][j]$ $=$ $n$일 동안 지각을 총 $i$번 하고, 연속으로 결석한 횟수가 $j$번 일 때의 상태

 

이렇게 상태를 정의하면 총 6개의 상태가 나올 수 있다는 사실을 우리는 잘 안다.

 

$\mathrm{DP}[N][0][0]$ $=$ $N$일동안 지각을 한번도 안하고 연속 결석 $0$번일때
$\mathrm{DP}[N][0][1]$ $=$ $N$일동안 지각을 한번도 안하고 연속 결석 $1$번일때
$\mathrm{DP}[N][0][2]$ $=$ $N$일동안 지각을 한번도 안하고 연속 결석 $2$번일때
$\mathrm{DP}[N][1][0]$ $=$ $N$일동안 지각 한번 하고 연속 결석 $0$번일때
$\mathrm{DP}[N][1][1]$ $=$ $N$일동안 지각 한번 하고 연속 결석 $1$번일때
$\mathrm{DP}[N][1][2]$ $=$ $N$일동안 지각 한번 하고 연속 결석 $2$번일때

 

여기서 확인할 수 있는 각각의 상태 전이는 다음과 같다.

 

$N+1$일 동안 지각 한번도 안하고 연속 결석 $0$번인 상태는 $N$일 동안 지각 한번도 안하고 연속 결석 $0$번, $1$번, $2$번인 상태에서 각각 출석하면 되므로, 아래와 같은 식이 나온다.


$$\mathrm{DP}[N+1][0][0] = \mathrm{DP}[N][0][0] + \mathrm{DP}[N][0][1] + \mathrm{DP}[N][0][2]$$

 

$N+1$일 동안 지각 한번도 안하고 연속 결석 $1$번인 상태는 $N$일 동안 지각 한번도 안하고 연속 결석 $0$번인 상태에서 결석하면 되므로, 아래와 같은 식이 나온다.

 

$$\mathrm{DP}[N+1][0][1] = \mathrm{DP}[N][0][0]$$

$N+1$일 동안 지각 한번도 안하고 연속 결석 $2$번인 상태는 $N$일 동안 지각 한번도 안하고 연속 결석 $1$번인 상태에서 결석하면 되므로, 아래와 같은 식이 나온다.


$$\mathrm{DP}[N+1][0][2] = \mathrm{DP}[N][0][1]$$


$N+1$일 동안 지각 한번 하고 연속 결석 $0$번인 상태는 $N$일 동안 지각 한번 한 상태에서 출석 하거나, $N$일 동안 지각 한번도 하지 않은 상태에서 지각하면 되므로, 아래와 같은 식이 나온다. 


$$\mathrm{DP}[N+1][1][0] = (\mathrm{DP}[N][1][0] + \mathrm{DP}[N][1][1] + \mathrm{DP}[N][1][2] + \mathrm{DP}[N][0][0] + \mathrm{DP}[N][0][1] + \mathrm{DP}[N][0][2])$$

$N+1$일 동안 지각 한번 하고 연속 결석 $1$번인 상태가 되려면 $N$일 동안 지각 한번 하고 연속 결석 $0$번인 상태에서 결석을 하면 되므로, 아래와 같은 식이 나온다.


$$\mathrm{DP}[N+1][1][1] = \mathrm{DP}[N][1][0]$$

$N+1$일 동안 지각 한번 하고 연속 결석 $2$번인 상태가 되려면 $N$일 동안 지각 한번 하고 연속 결석 $1$번인 상태에서 결석 하면 되므로, 아래와 같은 식이 나온다.


$$\mathrm{DP}[N+1][1][2] = \mathrm{DP}[N][1][1]$$

 

이를 통해 주어진 제한 범위인 $N = 1000$까지의 $\mathrm{DP}$값을 미리 구해놓은 다음 출력하면 문제를 해결할 수 있다.


아래는 내가 위의 접근 방식과 같이 작성한 파이썬 코드이다. 더보기를 누르면 확인할 수 있다.

더보기
# 1563번 개근상
# 다이나믹 프로그래밍
'''
접근 방법:
DP[N][0][0] = N일동안 지각을 한번도 안하고 연속 결석 0번일때
DP[N][0][1] = N일동안 지각을 한번도 안하고 연속 결석 1번일때
DP[N][0][2] = N일동안 지각을 한번도 안하고 연속 결석 2번일때
DP[N][1][0] = N일동안 지각 한번 하고 연속 결석 0번일때
DP[N][1][1] = N일동안 지각 한번 하고 연속 결석 1번일때
DP[N][1][2] = N일동안 지각 한번 하고 연속 결석 2번일때

지각 한번도 안하고 연속 결석 0번인 상태는
지각 한번도 안하고 연속 결석 0번, 1번, 2번인 상태에서 각각 출석하면 되므로
DP[N+1][0][0] = DP[N][0][0] + DP[N][0][1] + DP[N][0][2]

지각 한번도 안하고 연속 결석 1번인 상태는
지각 한번도 안하고 연속 결석 0번인 상태에서 결석하면 되므로
DP[N+1][0][1] = DP[N][0][0]

지각 한번도 안하고 연속 결석 2번인 상태는
지각 한번도 안하고 연속 결석 1번인 상태에서 결석하면 되므로
DP[N+1][0][2] = DP[N][0][1]


연속 결석 2번, 또는 1번, 또는 0번인 상태에서 출석 하면 연속 결석 0번인 상태이고,
지각 한번도 안한 상태에서 지각 한번 하면 연속 결석이 초기화되고 넘어갈 수 있으므로
DP[N+1][1][0] = (DP[N][1][0] + DP[N][1][1] + DP[N][1][2]
                 DP[N][0][0] + DP[N][0][1] + DP[N][0][2])

지각 이미 한 상태에서 연속 결석 1번인 상태가 되려면 연속 결석 0번인 상태에서 결석을 하면 됨.
DP[N+1][1][1] = DP[N][1][0]

지각 이미 한 상태에서 연속 결석 2번인 상태가 되려면 연속 결석 1번인 상태에서 결석을 하면 됨.
DP[N+1][1][2] = DP[N][1][1]

이렇게 3차원 DP를 돌리면 된다.
'''
# 전처리 과정
MAX = 1000
MOD = 1000000
DP = [[[0 for _ in range(3)] for _ in range(2)] for _ in range(MAX+1)]
DP[1][0][0], DP[1][0][1], DP[1][1][0] = 1, 1, 1
for N in range(1, MAX):
    DP[N+1][0][0] = (DP[N][0][0] + DP[N][0][1] + DP[N][0][2])%MOD
    DP[N+1][0][1] = DP[N][0][0]
    DP[N+1][0][2] = DP[N][0][1]
    DP[N+1][1][0] = (DP[N][1][0] + DP[N][1][1] + DP[N][1][2] +
                     DP[N][0][0] + DP[N][0][1] + DP[N][0][2])%MOD
    DP[N+1][1][1] = DP[N][1][0]
    DP[N+1][1][2] = DP[N][1][1]

n = int(input())
print((DP[n][1][0] + DP[n][1][1] + DP[n][1][2] + DP[n][0][0] + DP[n][0][1] + DP[n][0][2])%MOD)

'알고리즘 > 백준 문제 풀이' 카테고리의 다른 글

[Python] 9718번 Matrix Transformation  (0) 2023.03.14
[Python] 2758번 로또  (0) 2023.03.13
[Python] 1229번 육각수  (0) 2023.03.13
[Python] 2421번 저금통  (0) 2023.03.13
[Python] 22770번 Ellipse Intersection  (0) 2023.01.04