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

 

11052번: 카드 구매하기

첫째 줄에 민규가 구매하려고 하는 카드의 개수 N이 주어진다. (1 ≤ N ≤ 1,000) 둘째 줄에는 Pi가 P1부터 PN까지 순서대로 주어진다. (1 ≤ Pi ≤ 10,000)

www.acmicpc.net


문제 해결을 위한 과정

이 문제의 경우 dp 문제로써 점화식을 세워 i번째에 올 max값을 구하면 해결할 수 있는 문제입니다. 소스코드를 작성하면 다음과 같습니다.


소스코드 - python
1
2
3
4
5
6
7
8
9
10
11
= int(input())
data = list(map(int, input().split()))
dp = [0* n
dp[0= data[0]
 
for i in range(1, n):
  dp[i] = max(data[i], dp[i-1+ dp[0])
  for j in range(i//2+1):
    dp[i] = max(dp[i], dp[j] + dp[i-j-1])
 
print(dp[n-1]
cs

소스코드 - c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#define MAX 1001
#include <bits/stdc++.h>
 
using namespace std;
 
int main(void) {
    int n, dp[MAX], data[MAX];
    scanf("%d"&n);
 
 
    for (int i = 0; i < n; i++) {
        scanf("%d"&data[i]);
    }
    dp[0= data[0];
 
    for (int i = 1; i < n; i++) {
        dp[i] = max(data[i], dp[i - 1+ dp[0]);
        for (int j = 0; j < i / 2 + 1; j++) {
            dp[i] = max(dp[i], dp[j] + dp[i - j - 1]);
        }
     }
 
    printf("%d", dp[n - 1]);
    return 0;
}
cs

 

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

 

9461번: 파도반 수열

오른쪽 그림과 같이 삼각형이 나선 모양으로 놓여져 있다. 첫 삼각형은 정삼각형으로 변의 길이는 1이다. 그 다음에는 다음과 같은 과정으로 정삼각형을 계속 추가한다. 나선에서 가장 긴 변의

www.acmicpc.net


문제 해결을 위한 과정

문제에서 주어진 10개의 숫자를 통해 규칙을 찾을 수 있었던 문제였습니다. 표로 정리하면 다음과 같습니다.

p(1) p(2) p(3) p(4) p(5) p(6) p(7) p(8) p(9) p(10)
1 1 1 2 2 3 4 5 7 9

p(10)이후로 삼각형을 더 그려서도 파악할 수 있지만 규칙은 p(i) = p(i-1)+p(i-5) 인것을 알 수 있다. 따라서 다음의 규칙을 소스코드로 작성하면 다음과 같다.


소스코드 - python
1
2
3
4
5
6
7
8
9
10
11
12
13
tc = int(input())
for i in range(tc):
  n = int(input())
  dp = [0* (101)
  dp[0= 1
  dp[1= 1
  dp[2= 1
  dp[3= 2
  dp[4= 2
 
  for i in range(5, n+1):
    dp[i] = dp[i-1+ dp[i-5]
  print(dp[n-1])
cs

소스코드 - c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <bits/stdc++.h>
#define MAX 101
using namespace std;
 
int main(void) {
  int tc;
  scanf("%d",&tc);
 
  for(int i = 0; i < tc; i++) {
    int n;
    scanf("%d"&n);
    long long dp[MAX] = {0, };
    dp[0= 1;
    dp[1= 1;
    dp[2= 1;
    dp[3= 2;
    dp[4= 2;
 
    for(int j = 5; j < n+1; j++) {
      dp[j] = dp[j-1+ dp[j-5];
    }
 
    printf("%lld\n", dp[n-1]);
  }
  return 0;
}
cs

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

 

2193번: 이친수

0과 1로만 이루어진 수를 이진수라 한다. 이러한 이진수 중 특별한 성질을 갖는 것들이 있는데, 이들을 이친수(pinary number)라 한다. 이친수는 다음의 성질을 만족한다. 이친수는 0으로 시작하지 않

www.acmicpc.net


문제 해결을 위한 과정

이 문제는 규칙성을 파악하면 쉽게 해결할 수 있는 문제였습니다. n에 따른 이친수는 각각 다음과 같습니다.

n = 1 일때, 1 즉 1개

n = 2 일때, 10 즉 1개

n = 3 일때, 100, 101 즉 2개

n = 4 일때, 1000, 1001, 1010 즉 3개

n = 5 일때, 10000, 10001, 10010, 10100, 10101 즉 5개

 

따라서 이는 피보나치 수열의 형태임을 알 수 있다. 이를 소스코드로 옮기면 다음과 같다.


소스코드 - python
1
2
3
4
5
6
7
8
9
= int(input())
dp = [0* (n+1)
dp[0= 1
dp[1= 1
 
for i in range(2, n+1):
  dp[i] = dp[i-1+ dp[i-2]
 
print(dp[n-1])
cs

소스코드 - c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <bits/stdc++.h>
#define MAX 91
using namespace std;
 
int main(void) {
  int n;
  scanf("%d"&n);
 
  long long dp[MAX] = {0, };
 
  dp[0= 1;
  dp[1= 1;
 
  for(int i = 2; i < n+1; i++) {
    dp[i] = dp[i-1+ dp[i-2];
  }
 
  printf("%lld", dp[n-1]);
 
  return 0;
}
cs

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

 

11057번: 오르막 수

오르막 수는 수의 자리가 오름차순을 이루는 수를 말한다. 이때, 인접한 수가 같아도 오름차순으로 친다. 예를 들어, 2234와 3678, 11119는 오르막 수이지만, 2232, 3676, 91111은 오르막 수가 아니다. 수

www.acmicpc.net


문제 해결을 위한 과정

이 문제의 경우 끝자리를 기준으로 행렬을 만들어 표현하여 규칙을 찾는다면 쉽게 해결할 수 있는 문제였습니다.

다음의 표를 기준으로 설명하겠습니다. 각각 0, 1, 2, 3, 4, 5, 6, 7, 8, 9로 끝나는 경우를 n이 1일 때, 2일 때, 3일 때로 구분한 것입니다.

위의 표를 보시면 쉽게 알 수 있는데 바로 arr[i][j] = arr[i-1][j] + arr[i][j-1]입니다. 따라서 이를 소스코드에 적용하면 다음과 같습니다.


소스코드 - python
1
2
3
4
5
6
7
8
9
= int(input())
 
dp = [[1* 10 for _ in range(n)]
 
for i in range(1, n):
  for j in range(110):
    dp[i][j] = (dp[i][j-1+ dp[i-1][j])%10007
 
print(sum(dp[n-1])%10007)
cs

소스코드 - c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <bits/stdc++.h>
#define MAX 1001
#define MOD 10007
using namespace std;
 
int main(void) {
    int n;
    int result = 0;
    scanf("%d"&n);
    int dp[MAX][10];
    fill(&dp[0][0], &dp[MAX-1][10], 1); // 배열 전체 1로 초기화
 
    for (int i = 1; i < n; i++) {
        for (int j = 1; j < 10; j++)
            dp[i][j] = (dp[i][j - 1+ dp[i - 1][j]) % MOD;
    }
 
    for (int i = 0; i < 10; i++) {
        result += dp[n - 1][i];
    }
 
    printf("%d", result%MOD);
 
    return 0;
}
cs

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

 

9251번: LCS

LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다. 예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.

www.acmicpc.net


문제 해결을 위한 과정

이 문제는 행렬을 이용하여 기준이 되는 문자열과 다른 문자열을 한 글자씩 증가해가며 LCS를 구하는 문제였습니다. 행렬을 그린 후 하나씩 작성해 나가다 보면 규칙을 찾을 수 있습니다.

아래의 표에서 빨간색으로 칠한 3행 4열을 보시면 ACAYK에 CAPC의 LCS가 2라는 것을 알 수 있습니다.

이는 ACAYK CAPC 이기 때문입니다. 같은 원리로 5행4열을 보시면 ACAYK에 CAPCAK의 LCS가 4인 것을 알 수 있는데 이는 ACAYK CAPCAK이기 때문입니다. 

다시 위의 표를 보시면 규칙을 찾을 수 있는데 바로 위의 행이 그대로 아래의 행으로 내려온 후 비교해나가다가 같은 글자를 만나면 좌측 + 1이 된다는 것입니다. 다른 글자의 경우 max(좌, 상)이 들어가는 것을 확인할 수 있습니다. 

다른 글자의 경우 max(좌, 상)이 이해가 안 될 수 있어서 다른 예시를 첨부하면 다음과 같습니다.

2행4열을 보면 다음과 같이 P와 T가 다르기 때문에 max(1, 2)인 2가 들어간 것을 확인할 수 있습니다. 따라서 이를 코드로 옮기면 됩니다.


소스코드 - python

(코드 작성의 편의를 위해 행과 열을 하니씩 추가 하였습니다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
word1 = input()
word2 = input()
 
= len(word1)
= len(word2)
 
data = [[0* (m+1for _ in range(n+1)]
 
for i in range(1, n+1):
  for j in range(1, m+1):
    data[i][j] = data[i-1][j]
 
  for j in range(1, m+1):
    if word1[i-1== word2[j-1]:
      data[i][j] = data[i-1][j-1+ 1
    else:
      data[i][j] = max(data[i][j-1], data[i-1][j])
 
print(data[n][m])
cs

소스코드 - c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <bits/stdc++.h>
#define MAX 1001
using namespace std;
 
int main(void) {
  char word1[MAX];
  char word2[MAX];
  scanf("%s", word1);
  scanf("%s", word2);
 
  int n = strlen(word1);
  int m = strlen(word2);
 
  int data[MAX][MAX] = {0};
 
  for (int i = 1; i < n+1; i++) {
    for (int j = 1; j < m+1; j++) {
      data[i][j] = data[i-1][j];
    }
 
    for (int j = 1; j < m+1; j++) {
      if (word1[i-1== word2[j-1])
        data[i][j] = data[i-1][j-1+ 1;
 
      else
        data[i][j] = max(data[i][j-1], data[i-1][j]);
    }
  }
 
  printf("%d\n", data[n][m]);
  return 0;
}
cs

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

 

1904번: 01타일

지원이에게 2진 수열을 가르쳐 주기 위해, 지원이 아버지는 그에게 타일들을 선물해주셨다. 그리고 이 각각의 타일들은 0 또는 1이 쓰여 있는 낱장의 타일들이다. 어느 날 짓궂은 동주가 지원이

www.acmicpc.net


문제 해결을 위한 과정

이 문제의 경우 규칙성을 파악하면 쉽게 해결할 수 있는 문제였습니다. 또한 간단한 확률/통계에 관한 지식이 있다면 더욱 쉽게 해결할 수 있는 문제였습니다. 각각 n의 경우에 따라 계산해봅시다.

 

n = 1인 경우 1 -> 총 1개

n = 2인 경우 11, 00  -> 총 2개

n = 3인 경우 111, 100, 001 -> 총 3개

n = 4인 경우 1111, 1100, 1001, 0011, 0000 -> 총 5개

n = 5인 경우 11111, 11100, 11001, 10011, 00111, 10000, 00100, 00001 -> 총 8개

n = 6인 경우 111111, 110000, 100100, 100001, 000011, 001100, 001001, 111100, 111001, 110011, 100111, 001111, 000000 -> 총 13개

 

즉 피보나치수열의 형태를 띄고 있는 것을 알 수 있습니다. 따라서 점화식은 다음과 같습니다.

dp [i] = dp [i-1] + dp [i-2]


이번 문제부터 소스코드를 파이썬, C++ 두 가지 형태로 업로드할 계획입니다. 

소스코드 - python
1
2
3
4
5
6
7
8
9
= int(input())
dp = [0* (n+1)
dp[0= 1
dp[1= 1
 
for i in range(2, n+1):
  dp[i] = dp[i-1+ dp[i-2]
 
print(dp[n-1])
cs

소스코드 - C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <bits/stdc++.h>
 
using namespace std;
 
int main(void) {
  int n;
  scanf("%d"&n);
  int dp[1000001= {0};
 
  dp[0= 1;
  dp[1= 2;
  for(int i = 2; i < n+1; i++) {
    dp[i] = (dp[i-1+ dp[i-2])%15746;
  }
 
  printf("%d\n", dp[n-1]);
 
  return 0;
}
cs

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

 

2156번: 포도주 시식

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

www.acmicpc.net


문제 해결을 위한 과정

이 문제 역시 전형적인 dp로 해결할 수 있는 문제였습니다. 계단 오르기 문제(https://www.acmicpc.net/problem/2579)와 거의 유사하였으나 계단 오르기 문제는 꼭 마지막을 밟아야 한다는 조건이 있었던 반면 이 문제의 경우는 마지막 포도주를 꼭 먹어야 한다는 조건이 존재하지 않습니다. 따라서 max 조건에서 data[i] + dp[i-2], data[i] + data[i-1] + dp[i-3]과 함께 dp[i-1] 조건 역시 비교해야 합니다. 따라서 이를 소스코드로 작성하면 다음과 같습니다.


소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
= int(input())
data = [0* 10001
dp = [0* 10001
 
for i in  range(n):
  data[i] = int(input())
 
dp[0= data[0]
dp[1= data[0+ data[1]
dp[2= max(data[0+ data[1], data[0+ data[2], data[1]+ data[2])
 
for i in range(3, n):
  dp[i] = max(data[i] + dp[i-2], data[i] + data[i-1+ dp[i-3], dp[i-1])
 
print(max(dp))
cs

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

 

1912번: 연속합

첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.

www.acmicpc.net


문제 해결을 위한 과정

이 문제의 경우 연속적으로 순자를 선택한 후 그 합이 최대가 되도록 하면 해결할 수 있는 dp 문제입니다. 따로 dp 리스트를 만들기보다 입력받은 리스트를 그대로 활용하면 보다 간단하게 해결할 수 있는 문제였습니다. 먼저 예시를 통해 알아봅시다.

 

0번째 인덱스까지의 최대의 합은 0번째 인덱스 그대로입니다.

10 -4 3 1 5 6 -35 12 21 -1

 

1번째 인덱스까지의 최대의 합은 1번째 인덱스 그대로가 아닌 0번째 + 1번째입니다.

10 6 3 1 5 6 -35 12 21 -1

 

2번째 인덱스까지의 최대의 합은 2번째 인덱스 그대로가 아닌 0번째 + 1번째 + 2번째입니다.

10  6 9 1 5 6 -35 12 21 -1

 

위와 같은 방식으로 6번째 인덱스 까지는 인덱스 그대로가 아닌 합으로 이루어집니다. 이는 아래의 그림과 같습니다.

10 6 9 10 15 21 -14 12 21 -1

 

다만, 7번째 인덱스까지의 최대의 합은 0번째부터 7번째까지의 합이 아닌 7번째 인덱스 그 자체입니다. 따라서 7번째 인덱스는 그대로 유지됩니다.

10 6 9 10 15 21 -14 12 21 -1

 

8번째 인덱스까지의 최대의 합은 8번째 그대로가 아닌 7번째 + 8번째입니다.

10 6 9 10 15 21 -14 12 33 -1

 

9번째 인덱스까지의 최대의 합은 9번째 그대로가 아닌 7번째 + 8번째 + 9번째입니다. 위의 표를 기반으로 점화식을 작성하면 다음과 같습니다.

 

data[i] = max(data[i], data[i-1] + data[i])

이를 소스코드로 표현하면 다음과 같습니다.


소스코드
1
2
3
4
5
6
7
= int(input())
data = list(map(int, input().split()))
 
for i in range(1, n):
  data[i] = max(data[i], data[i] + data[i-1])
  
print(max(data))
cs

 

+ Recent posts