728x90

단계별로 풀어보기 동적 계획법 1의 10단계 문제

 

2156번: 포도주 시식

효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규칙이 있다. 포도주 잔을 선택하면 그 잔에 들어있는 포도주는 모두 마셔야 하고, 마신 후에는 원래 위치에 다시 놓아야 한다. 연속으로 놓여 있는 3잔을 모두 마실 수는 없다. 효주는 될 수 있는 대로 많은 양의 포도주를 맛보기 위해서 어떤 포도주 잔을 선택해야 할지 고

www.acmicpc.net

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

int main() {
	int n;
	cin >> n;
	int* grape = new int[n];
	int** dp = new int*[n];
	for (int i = 0; i < n; i++) {
		dp[i] = new int[2];
	}
	for (int i = 0; i < n; i++) {
		cin >> grape[i];
	}
	dp[0][0] = grape[0];
	dp[0][1] = grape[0];
	if (n > 1) {
		dp[1][0] = grape[0] + grape[1];
		dp[1][1] = grape[1];
	}
	for (int i = 2; i < n; i++) {
		dp[i][0] = dp[i - 1][1] + grape[i];
		int max = 0;
		for (int j = i - 2; j >= 0; j--) {
			for (int k = 0; k < 2; k++) {
				if (dp[j][k] > max) {
					max = dp[j][k];
				}
			}
		}
		dp[i][1] = max + grape[i];
	}
	int max = 0;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < 2; j++) {
			if (dp[i][j] > max) {
				max = dp[i][j];
			}
		}
	}
	cout << max << endl;
	return 0;
}

배열 dp[i][0]에는 i-1번째 포도주를 마셨을 경우 최대 양을, dp[i][1]에는 i-1번째 포도주를 마시지 않았을 경우 가능한 최대 양을 저장했다.

2579번 계단오르기와 다른 점은 계단 오르기 문제는 반드시 마지막 계단을 밟아야 하지만 이 문제는 반드시 마지막 포도주를 마실 필요가 없다. 또, 계단오르기는 무조건 한 칸이나 두 칸씩 올라야 하지만 포도주 마시기는 여러 포도주를 뛰어넘고 안 마셔도 상관이 없다. 따라서 이 문제가 더 고려할 것이 많아 조금 더 복잡하다.

 

이렇게 2020년에 1월 24일에 풀고 두 달 반 후인 4월 11일에 다시 한 번 풀어보았다.

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


int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	int n;
	cin >> n;
	vector<int> v(n);
	for (int i = 0; i < n; i++) {
		cin >> v[i];
	}
	vector<tuple<int, int, int>> dp(n);
	dp[0] = make_tuple(0, v[0], v[0]);
	if (n > 1) {
		dp[1] = make_tuple(v[0], v[1], v[0] + v[1]);
	}
	for (int i = 2; i < n; i++) {
		get<0>(dp[i]) = max(get<0>(dp[i - 1]), max(get<1>(dp[i - 1]), get<2>(dp[i - 1])));
		get<1>(dp[i]) = get<0>(dp[i - 1]) + v[i];
		get<2>(dp[i]) = get<1>(dp[i - 1]) + v[i];
	}
	cout << max(get<0>(dp[n - 1]), max(get<1>(dp[n - 1]), get<2>(dp[n - 1])));
	return 0;
}

코드 길이도 더 짧고 시간도 더 적게 걸렸고 메모리도 더 적게 사용했다.

나는 잘 모르겠어도 나도 모르는 사이에 발전은 하나보다.

3-tuple을 만들어서 dp 벡터에 저장했다.

dp[i]에 저장된 튜플의 첫 번째 원소는 i번째 포도주를 마시지 않는 경우 중 최댓값을 저장한다. i번째 포도주를 마시지 않으므로 dp[i]에 저장된 3가지 숫자 중에 가장 큰 수를 그대로 가져온다.

dp[i]에 저장된 튜플의 두 번째 원소는 i번째 포도주를 마시되 i - 1번째 포도주는 마시지 않았을 경우 중 최댓값을 저장한다. i - 1번째 포도주는 마시지 않았어야 하므로 dp[i - 1]의 첫 번째 원소에 i번째 포도주를 더해준 값을 저장한다.

dp[i]에 저장된 튜플의 세 번째 원소는 i번째 포도주 연속으로 마시는 경우이다. 세 개를 연속으로 마시지는 못하므로 dp[i - 1]의 두 번째 원소에 i번째 포도주를 더해준 값을 저장한다.

728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 2580번 스도쿠  (0) 2020.01.25
[백준] 2775번 부녀회장이 될테야  (0) 2020.01.24
[백준] 2579번 계단 오르기  (0) 2020.01.24
[백준] 2446번 별 찍기 - 9  (0) 2020.01.22
[백준] 2445번 별 찍기 - 8  (0) 2020.01.22
728x90

단계별로 풀어보기 동적 계획법 1의 7단계 문제이다.

 

2579번: 계단 오르기

계단 오르기 게임은 계단 아래 시작점부터 계단 꼭대기에 위치한 도착점까지 가는 게임이다. <그림 1>과 같이 각각의 계단에는 일정한 점수가 쓰여 있는데 계단을 밟으면 그 계단에 쓰여 있는 점수를 얻게 된다. 예를 들어 <그림 2>와 같이 시작점에서부터 첫 번째, 두 번째, 네 번째, 여섯 번째 계단을 밟아 도착점에 도달하면 총 점수는 10 + 20 + 25 + 20 = 75점이 된다. 계단 오르는 데는 다음과 같은 규칙이 있다. 계단은 한 번에 한 계단씩

www.acmicpc.net

오랜만에 C++로 풀었는데 벡터가 아니라 동적할당을 이용해서 입력을 받았다.

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

int main() {
	int n;
	cin >> n;
	int* stair = new int[n];
	int** score = new int*[n];
	for (int i = 0; i < n; i++) {
		score[i] = new int[2];
	}
	for (int i = 0; i < n; i++) {
		cin >> stair[i];
	}
	if (n > 0) {
		score[0][0] = stair[0];
		score[0][1] = stair[0];
	}
	if (n > 1) {
		score[1][0] = stair[0] + stair[1];
		score[1][1] = stair[1];
	}
	if (n > 2) {
		score[2][0] = stair[1] + stair[2];
		score[2][1] = stair[0] + stair[2];
	}

	for (int i = 3; i < n; i++) {
		score[i][0] = max(score[i - 3][1] + stair[i - 1] + stair[i], score[i - 3][0] + stair[i - 1] + stair[i]);
		score[i][1] = max(score[i - 3][1] + stair[i - 2] + stair[i], score[i - 2][1] + stair[i]);
	}
	cout << max(score[n - 1][0], score[n - 1][1]) << endl;
	return 0;
}

stair 배열에는 입력으로 들어온 각 계단의 점수를 저장하고 score 배열에는 현재 그 칸까지 올라왔을 때 최대로 얻을 수 있는 점수를 저장했다.

score는 이차원배열인데 score[i][0]에는 i-1번째 계단을 밟고 i번째 계단까지 올라왔을 때의 얻을 수 있는 최대 점수이고 score[i][1]에는 i-1번째 계단을 밟지 않고 i번째 계단까지 올라왔을 때 얻을 수 있는 최대 점수를 저장했다. 

 

동적할당으로 이차원배열을 선언할 때에는 이렇게 하면 된다.

int** score = new int*[n];
for (int i = 0; i < n; i++) {
	score[i] = new int[2];
}
728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 2775번 부녀회장이 될테야  (0) 2020.01.24
[백준] 2156번 포도주 시식  (0) 2020.01.24
[백준] 2446번 별 찍기 - 9  (0) 2020.01.22
[백준] 2445번 별 찍기 - 8  (0) 2020.01.22
[백준] 2444번 별 찍기 - 7  (0) 2020.01.22
728x90
 

2446번: 별 찍기 - 9

첫째 줄부터 2×N-1번째 줄까지 차례대로 별을 출력한다.

www.acmicpc.net

#include <iostream>
using namespace std;

int main() {
	int n;
	cin >> n;
	for (int i = 0; i < 2 * n - 1; i++) {
		for (int j = 0; j < n - 1 - abs(n - 1 - i); j++) {
			cout << " ";
		}
		for (int j = 0; j < 2 * abs(n - 1 - i) + 1; j++) {
			cout << "*";
		}
		cout << endl;
	}
	return 0;
}
728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 2156번 포도주 시식  (0) 2020.01.24
[백준] 2579번 계단 오르기  (0) 2020.01.24
[백준] 2445번 별 찍기 - 8  (0) 2020.01.22
[백준] 2444번 별 찍기 - 7  (0) 2020.01.22
[백준] 2443번 별 찍기 - 6  (0) 2020.01.22
728x90
 

2445번: 별 찍기 - 8

첫째 줄부터 2×N-1번째 줄까지 차례대로 별을 출력한다.

www.acmicpc.net

#include <iostream>
using namespace std;

int main() {
	int n;
	cin >> n;
	for (int i = 0; i < 2 * n - 1; i++) {
		for (int j = 0; j < n - abs(n - 1 - i); j++) {
			cout << "*";
		}
		for (int j = 0; j < 2 * abs(n - 1 - i); j++) {
			cout << " ";
		}
		for (int j = 0; j < n - abs(n - 1 - i); j++) {
			cout << "*";
		}
		cout << endl;
	}
	return 0;
}
728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 2579번 계단 오르기  (0) 2020.01.24
[백준] 2446번 별 찍기 - 9  (0) 2020.01.22
[백준] 2444번 별 찍기 - 7  (0) 2020.01.22
[백준] 2443번 별 찍기 - 6  (0) 2020.01.22
[백준] 1463번 1로 만들기  (0) 2020.01.21
728x90
 

2444번: 별 찍기 - 7

첫째 줄부터 2×N-1번째 줄까지 차례대로 별을 출력한다.

www.acmicpc.net

#include <iostream>
using namespace std;

int main() {
	int n;
	cin >> n;
	for (int i = 0; i < 2 * n - 1; i++) {
		for (int j = 0; j < abs(n - i - 1); j++) {
			cout << " ";
		}
		for (int j = 0; j < 2 * n - 1 - 2 * (abs(n - 1 - i)); j++) {
			cout << "*";
		}
		cout << endl;
	}
	return 0;
}
728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 2446번 별 찍기 - 9  (0) 2020.01.22
[백준] 2445번 별 찍기 - 8  (0) 2020.01.22
[백준] 2443번 별 찍기 - 6  (0) 2020.01.22
[백준] 1463번 1로 만들기  (0) 2020.01.21
[백준] 1261번 (시간 초과)  (0) 2020.01.21
728x90
 

2443번: 별 찍기 - 6

첫째 줄에는 별 2×N-1개, 둘째 줄에는 별 2×N-3개, ..., N번째 줄에는 별 1개를 찍는 문제 별은 가운데를 기준으로 대칭이어야 한다.

www.acmicpc.net

#include <iostream>
using namespace std;

int main() {
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < i; j++) {
			cout << " ";
		}
		for (int j = 0; j < 2 * n - 2 * i - 1; j++) {
			cout << "*";
		}
		cout << endl;
	}
	return 0;
}
728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 2445번 별 찍기 - 8  (0) 2020.01.22
[백준] 2444번 별 찍기 - 7  (0) 2020.01.22
[백준] 1463번 1로 만들기  (0) 2020.01.21
[백준] 1261번 (시간 초과)  (0) 2020.01.21
[백준] 1475번 방 번호  (0) 2020.01.20
728x90

단계별로 풀어보기 동적계획법 1의 8번 문제

 

1463번: 1로 만들기

첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.

www.acmicpc.net

2020년 1월 21일에 파이썬으로 풀고

n = int(input())
count = 0
li = [0] * (n + 1)
for i in range(2, n + 1):
    temp = []
    if i % 3 == 0:
        temp.append(li[i // 3] + 1)
    if i % 2 == 0:
        temp.append(li[i // 2] + 1)
    temp.append(li[i - 1] + 1)
    li[i] = min(temp)
print(li[n])

두 달 반 뒤인 2020년 4월 6일에 C++로 다시 풀어보았다.

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


int main() {
	int x;
	cin >> x;
	vector<int> dp(x + 1);
	dp[1] = 0;
	for (int i = 2; i <= x; i++) {
		vector<int> v;
		v.push_back(dp[i - 1]);
		if (i % 2 == 0)
			v.push_back(dp[i / 2]);
		if (i % 3 == 0)
			v.push_back(dp[i / 3]);
		dp[i] = *min_element(v.begin(), v.end()) + 1;
	}
	cout << dp[x];
	return 0;
}

역시 C++이 효율적인거로는 최고인 것 같다. 파이썬의 10분의 1도 안 걸렸다.

 

풀이법을 설명하자면

더 큰 수로 나눌 수록 무조건 좋은 건 아니다. 3으로 나누는게 2로 나누는 것보다 더 많이 줄어들기 때문에, 2로 나누는게 1로 빼는 것보다 더 많이 줄어들기 때문에 큰 수로 나눌수록 좋다고 생각할 수도 있는데 그렇지 않다.

예를 들어 10의 경우 10을 2로 나누면 10 -> 5 -> 4 -> 2 -> 1 이렇게 네 번만에 1이 되지만

10에서 1을 먼저 빼면 10 -> 9 -> 3 -> 1 이렇게 세 번만에 1을 만들 수 있다.

따라서 10 / 2인 5가 1로 빨리 가는지 10 - 1인 9가 1로 빨리 가는지 확인해보고 그것을 선택해야 한다.

즉 그것을 미리 구해놓아야 한다. 따라서 동적계획법을 사용해서 가장 작은 계산부터 해서 그 계산을 이용해서 더 큰 계산을 해나가면 된다. 1에서 i로 가는 데 걸리는 연산의 횟수를 구해서 dp[i]에 저장해놓고 dp[i + 1], dp[i * 2], dp[i * 3]은 dp[i] + 1 이런 식으로 구해나갔다. 출력은 dp[x]를 하면 된다.

코드에서 min_element() 함수의 사용법을 모른다면 아래 링크에 있는 포스팅을 참고하면 된다.

https://breakcoding.tistory.com/299

 

[C++] 벡터, 배열에서 최댓값, 최솟값 찾기 (min_element, max_element)

C++에서 두 수 a, b 중에 어떤 수가 더 큰지, 작은지 알고 싶다면 #include 으로 헤더를 추가하고 min(a, b); 또는 max(a, b); 이렇게 하면 된다. 하지만 배열이나 벡터의 원소들 중에서..

breakcoding.tistory.com

 

2021년 1월 6일 추가

또 다시 풀어보았다. 코딩을 오래 쉬다가 풀어서 그런가 방법을 생각하는데 하루종일 걸렸다. 그래도 끝까지 아무것도 안 보고 스스로 풀어냈다.

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

int main() {
	int n;
	cin >> n;
	vector<int> dp(n + 1);
	dp[1] = 0;
	for (int i = 2; i <= n; i++) {
		vector<int> temp;
		if (i % 3 == 0) {
			temp.push_back(dp[i / 3] + 1);
		}
		if (i % 2 == 0) {
			temp.push_back(dp[i / 2] + 1);
		}
		temp.push_back(dp[i - 1] + 1);
		dp[i] = *min_element(temp.begin(), temp.end());
	}
	cout << dp[n];
	return 0;
}

1부터 거꾸로 올라가는 것이다.

728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 2444번 별 찍기 - 7  (0) 2020.01.22
[백준] 2443번 별 찍기 - 6  (0) 2020.01.22
[백준] 1261번 (시간 초과)  (0) 2020.01.21
[백준] 1475번 방 번호  (0) 2020.01.20
[백준] 2747번 피보나치 수  (0) 2020.01.20
728x90
 

1261번: 알고스팟

첫째 줄에 미로의 크기를 나타내는 가로 크기 M, 세로 크기 N (1 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 미로의 상태를 나타내는 숫자 0과 1이 주어진다. 0은 빈 방을 의미하고, 1은 벽을 의미한다. (1, 1)과 (N, M)은 항상 뚫려있다.

www.acmicpc.net

예상대로 시간초과가 나왔다. (1, 1)부터 (N, M)으로 가는 모든 경우의 수를 다 구해서 그 경우 벽을 몇 번 부수는지 count라는 리스트에 담아놓은 뒤에 최솟값을 출력하도록 했다. 

예상대로 시간초과가 나왔다.

from sys import stdin
m, n = list(map(int, stdin.readline().split()))
li = []
for i in range(n):
    li.append(stdin.readline())
import collections
q = collections.deque()
q.append((0, 0, 0))
visited = [[False] * m for i in range(n)]
count = []
while q:
    cur = q.popleft()
    if cur[0] == n - 1 and cur[1] == m - 1:
        count.append(cur[2])
        if cur[2] == 0:
            break
        continue
    visited[cur[0]][cur[1]] = True
    if cur[1] + 1 < m and not visited[cur[0]][cur[1] + 1]:
        if li[cur[0]][cur[1] + 1] == '1':
            q.append((cur[0], cur[1] + 1, cur[2] + 1))
        else:
            q.append((cur[0], cur[1] + 1, cur[2]))
    if cur[1] - 1 >= 0 and not visited[cur[0]][cur[1] - 1]:
        if li[cur[0]][cur[1] - 1] == '1':
            q.append((cur[0], cur[1] - 1, cur[2] + 1))
        else:
            q.append((cur[0], cur[1] - 1, cur[2]))
    if cur[0] + 1 < n and not visited[cur[0] + 1][cur[1]]:
        if li[cur[0] + 1][cur[1]] == '1':
            q.append((cur[0] + 1, cur[1], cur[2] + 1))
        else:
            q.append((cur[0] + 1, cur[1], cur[2]))
    if cur[0] - 1 >= 0 and not visited[cur[0] - 1][cur[1]]:
        if li[cur[0] - 1][cur[1]] == '1':
            q.append((cur[0] - 1, cur[1], cur[2] + 1))
        else:
            q.append((cur[0] - 1, cur[1], cur[2]))
print(min(count))
728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 2443번 별 찍기 - 6  (0) 2020.01.22
[백준] 1463번 1로 만들기  (0) 2020.01.21
[백준] 1475번 방 번호  (0) 2020.01.20
[백준] 2747번 피보나치 수  (0) 2020.01.20
[백준] 5622번 다이얼  (0) 2020.01.20
728x90

백준에서 가장 많이 풀린 문제 TOP 100중 한 문제

 

1475번: 방 번호

첫째 줄에 다솜이의 방 번호 N이 주어진다. N은 1,000,000보다 작거나 같은 자연수 또는 0이다.

www.acmicpc.net

 

n = input()
count = {'0' : 0, '1' : 0, '2' : 0, '3' : 0, '4' : 0, '5' : 0, '6' : 0, '7' : 0, '8' : 0}
for i in n:
    if i == '0':
        count['0'] += 1
    elif i == '1':
        count['1'] += 1
    elif i == '2':
        count['2'] += 1
    elif i == '3':
        count['3'] += 1
    elif i == '4':
        count['4'] += 1
    elif i == '5':
        count['5'] += 1
    elif i == '6' or i == '9':
        count['6'] += 1
    elif i == '7':
        count['7'] += 1
    elif i == '8':
        count['8'] += 1
a, b = divmod(count['6'], 2)
if b == 1:
    count['6'] = count['6'] // 2 + 1
else:
    count['6'] = count['6'] // 2
max = 0
for i, j in count.items():
    if j > max:
        max = j
print(max)
728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 1463번 1로 만들기  (0) 2020.01.21
[백준] 1261번 (시간 초과)  (0) 2020.01.21
[백준] 2747번 피보나치 수  (0) 2020.01.20
[백준] 5622번 다이얼  (0) 2020.01.20
[백준] 1316번 그룹 단어 체커  (0) 2020.01.20
728x90

백준에서 가장 많이 풀린 문제 TOP 100중 한 문제

 

2747번: 피보나치 수

피보나치 수는 0과 1로 시작한다. 0번째 피보나치 수는 0이고, 1번째 피보나치 수는 1이다. 그 다음 2번째 부터는 바로 앞 두 피보나치 수의 합이 된다. 이를 식으로 써보면 Fn = Fn-1 + Fn-2 (n>=2)가 된다. n=17일때 까지 피보나치 수를 써보면 다음과 같다. 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597 n이 주어졌을 때, n번째 피보나치 수를 구하는

www.acmicpc.net

n = int(input())
li = [0] * (n + 1)
li[0] = 0
li[1] = 1
for i in range(2, n + 1):
    li[i] = li[i - 2] + li[i - 1]
print(li[n])
728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 1261번 (시간 초과)  (0) 2020.01.21
[백준] 1475번 방 번호  (0) 2020.01.20
[백준] 5622번 다이얼  (0) 2020.01.20
[백준] 1316번 그룹 단어 체커  (0) 2020.01.20
[백준] 2839번 설탕배달  (0) 2020.01.20

+ Recent posts