1. 문제

1) 문제 링크

    : www.acmicpc.net/problem/9252

 

9252번: LCS 2

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

www.acmicpc.net

 

2) 설명

 

2. 풀이

1) 알고리즘

    : DP

 

2) 풀이 설명

두 문자열이 주어질 때 가장 긴 공통 문자열의 길이와 그 문자열을 추출하는 문제.
ex) ABCDEG, BDAEFG 라면 BDEG가 가장 긴 공통 문자열이 되고 길이는 4가 된다.

첫 접근은 DP를 사용해서 공통 문자열의 길이를 구했다.
예를 들어 문자열 A, B가 주어지면 solve(A_idx, B_idx)를 재귀적으로 사용해서 가장 긴 문자열의 길이를 구하는 방식이다.
dp[x][y] 에는 A의 x번째 문자, B의 y번째 문자로부터 만들 수 있는 가장 긴 공통 문자열의 길이를 반환한다.
그러나 이 방식으로는 문자열의 길이는 구해도 어떤 문자열인지 추적하는 일이 너무 힘들었다.
그래서 검색 결과 다른 방식으로 DP를 사용하는 법을 배웠다.

일단 탑다운 방식보다는 바텀업 방식으로 진행해나갔다.

dp[i][j] 는 A의 i번째 문자까지와 B의 j-1번째 문자까지, B의 j번째 문자까지와 A의 i-1번째 문자까지 비교했을 때 가장 긴 공통 문자열의 길이를 의미한다.

그러므로 A[i-1] == B[j-1] 이라면 dp[i][j] = dp[i-1][j-1] + 1이 되고, 같지 않다면 dp[i][j] = max(dp[i-1][j], dp[i][j-1])이 된다.

이부분이 상당히 이해하기 까다로웠다.

내가 이해한 바로는 (A[i], B[j-1])로 만들 수 있는 가장 긴 공통 문자열의 길이, (A[i-1], B[j])로 만들 수 있는 가장 긴 공통 문자열의 길이는 (A[i-1], B[j-1])로 만들 수 있는 가장 긴 공통 문자열의 길이와 같거나 1개 더 크기 때문으로 이해했다.

이렇게 다 돌고나면 dp[A의 길이][B의 길이]가 가장 긴 공통 문자열의 길이가 된다.

 

이후 만들어놓은 자취를 따라서 역추적해서 정답 문자열을 추출해내면 된다.

3. 코드

더보기
/*
	LCS 2
	DP
	Top-down
    길이만 구할 수 있었다..
*/
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#define MAX 1000

using namespace std;

string A = "", B = ""; 
vector<vector<int>> dp(MAX, vector<int>(MAX, -1));

int solve(int A_idx, int B_idx){
	if(A_idx == A.length() || B_idx == B.length())
		return 0;
	if(dp[A_idx][B_idx]>=0)
		return dp[A_idx][B_idx];
	for(int i=A_idx;i<A.length();i++){
		for(int j=B_idx;j<B.length();j++){
			if(A[i] == B[j]){
				dp[A_idx][B_idx] = max(dp[A_idx][B_idx], solve(i+1, j+1)+1);
			}
		}
	}
	return dp[A_idx][B_idx];
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	
	cin >> A >> B;
	
	cout << solve(0, 0) << "\n";

	return 0;
}
/*
	LCS 2
	DP
	Bottom-up
*/
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#define MAX 1000

using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	
	string A = "", B = ""; 
	cin >> A >> B;
	
	vector<vector<int>> dp(MAX, vector<int>(MAX, 0));

	//길이 구하기
	for(int i=1;i<=A.length();i++){
		for(int j=1;j<=B.length();j++){
			if(A[i-1] == B[j-1]){
				dp[i][j] = dp[i-1][j-1] + 1;
			} else {
				dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
			}
		}
	}
	cout << dp[A.length()][B.length()] << endl;
	
	//경로 찾아가기
	string answer = "";
	
	int x = A.length();
	int y = B.length();
	
	while(dp[x][y]>0){
		if(dp[x][y] == dp[x-1][y]){
			x--;
		} else if(dp[x][y] == dp[x][y-1]){
			y--;
		} else {
			answer = A[x-1] + answer;
			x--;
			y--;
		}
	}
	cout << answer << endl;
	return 0;
}

 

4. 느낀점

 - 머리로는 대강 알겠는데 설명하는 게 너무 어렵다. 대강 이해한 게 문제인 것 같다.. 내 생각을 남들이 이해할 수 있도록 표현하는 연습을 하는 것도 중요하니까 어렵더라도 계속 설명하려 노력해야겠다.

 - 단순히 길이만을 구하는 탑다운 풀이에서 벗어나오지를 못했다... 안 될 것 같으면 고집부리지 말고 다른 방식의 풀이도 생각해보자.

'알고리즘 > 백준' 카테고리의 다른 글

[백준/9656] 돌 게임 2  (0) 2020.11.02
[백준/2631] 줄세우기  (0) 2020.11.02
[백준/11501] 주식  (0) 2020.10.18
[백준/15312] 이름 궁합  (1) 2020.10.18
[백준/17070] 파이프 옮기기 1  (0) 2020.10.18

1. 문제

1) 문제 링크

    : www.acmicpc.net/problem/11501

 

11501번: 주식

입력의 첫 줄에는 테스트케이스 수를 나타내는 자연수 T가 주어진다. 각 테스트케이스 별로 첫 줄에는 날의 수를 나타내는 자연수 N(2 ≤ N ≤ 1,000,000)이 주어지고, 둘째 줄에는 날 별 주가를 나타

www.acmicpc.net

 

2) 설명

일별 주가가 주어졌을 때 주식을 사고 팔아 얻을 수 있는 최대의 이윤을 구하는 문제.

하루에 주식 하나 이하로만 살 수 있다.

2. 풀이

1) 알고리즘

    : 단순 구현

 

2) 풀이 설명

 주식을 매수한 시점보다 미래에 더 높은 가격에 매도할 수만 있으면 그 시점에는 무조건 주식을 매수해야 한다.(파는 건 여러 개 매도할 수 있기 때문)

그래서 해당 시점(i)에 주식을 매수했을 때 가능한 가장 높은 매도가를 기록해놓는 sellPrice 벡터를 사용했다.

그리고 sellPrice[i] - stock[i](매도가 - 매수가)가 양수일 때만 answer (답)에 더해주었다.

 

3. 코드

더보기

 

/*
	주식
	단순 구현
*/
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	
	int T;

	cin >> T;

	while(T--){
		long long N, answer = 0;
		cin >> N;
		vector<long long> stock(N);
		vector<long long> sellPrice(N, 0);

		for(int i=0;i<N;i++)
			cin >> stock[i];
		
		for(int i=N-2;i>=0;i--)
			sellPrice[i] = max(sellPrice[i+1], stock[i+1]);

		for(int i=0;i<N;i++)
			answer += max((sellPrice[i] - stock[i]), (long long)0);
		cout << answer << endl;
	}

	return 0;
}

 

4. 느낀점

- 처음에 별 생각 없이 sellPrice를 O(n^2)으로 구했다가 시간초과 났다.. 시간 많이 준다고 해도 (5초) 효율성 고려하자..

'알고리즘 > 백준' 카테고리의 다른 글

[백준/2631] 줄세우기  (0) 2020.11.02
[백준/9252] LCS 2  (0) 2020.10.27
[백준/15312] 이름 궁합  (1) 2020.10.18
[백준/17070] 파이프 옮기기 1  (0) 2020.10.18
[백준/5557] 1학년  (1) 2020.10.18

1. 문제

1) 문제 링크

    : www.acmicpc.net/problem/15312

 

15312번: 이름 궁합

영어 대문자 알파벳 26개의 획수는 순서대로 3, 2, 1, 2, 3, 3, 2, 3, 3, 2, 2, 1, 2, 2, 1, 2, 2, 2, 1, 2, 1, 1, 1, 2, 2, 1 로 정한다. (출제자가 알파벳 대문자를 쓰는 방법이 기준이다)

www.acmicpc.net

 

2) 설명

 그냥 영어 이름 궁합 보는 문제다.

여자친구랑 이름 궁합 봐보려고 풀어봤는데 궁합 정확도가 상당히 떨어지는 것 같다.

 

2. 풀이

1) 알고리즘

    : 단순 구현

 

2) 풀이 설명

재귀를 사용해서 풀어보았다.

그냥 원소 두 개 남을 때까지 이웃 원소들의 합 벡터( (1,2), (2,3), (3,4), ... , (N-1,N)를 반환하는 함수를 계속 재귀 호출했다.

 

3. 코드

더보기
/*
	이름 궁합
	단순 구현
*/
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> solve(vector<int> nums){
	if(nums.size() == 2)
		return nums;
	vector<int> answer;
	for(int i=0;i<(int)nums.size()-1;i++)
		answer.push_back((nums[i]+nums[i+1])%10);

	return solve(answer);
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	
	vector<int> cnt = {3, 2, 1, 2, 3, 3, 2, 3, 3, 2, 2, 1, 2, 2, 1, 2, 2, 2, 1, 2, 1, 1, 1, 2, 2, 1};

	string jm, jh;
	cin >> jm >> jh;
	
	vector<int> nums;
	for(int i=0;i<jm.length();i++){
		nums.push_back(cnt[jm[i] - 'A']);
		nums.push_back(cnt[jh[i] - 'A']);
	}
	vector<int> result = solve(nums);
	for(auto r : result)
		cout << r;
	cout << endl;

	return 0;
}

 

4. 느낀점

- 재귀로 한번 풀어봤는데 비효율적이다.(시간적, 공간적 모두) 그렇지만 재미삼아 푼 문제라서 그냥 내비뒀다.

 -> 반복문으로 두 개씩 줄여가도록 풀면 빨라진다.

'알고리즘 > 백준' 카테고리의 다른 글

[백준/9252] LCS 2  (0) 2020.10.27
[백준/11501] 주식  (0) 2020.10.18
[백준/17070] 파이프 옮기기 1  (0) 2020.10.18
[백준/5557] 1학년  (1) 2020.10.18
[백준/1261] 알고스팟  (0) 2020.10.16

1. 문제

1) 문제 링크

    : www.acmicpc.net/problem/17070

 

17070번: 파이프 옮기기 1

유현이가 새 집으로 이사했다. 새 집의 크기는 N×N의 격자판으로 나타낼 수 있고, 1×1크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 (r, c)로 나타낼 수 있다. 여기서 r은 행의 번호, c는 열의

www.acmicpc.net

 

2) 설명

길이 2의 파이프가 있다.

파이프는 가로, 세로, 대각선(좌상단 -> 우하단) 방향으로 밀 수 있다.

처음에 (1, 1), (1, 2)에 놓여진 파이프를 (N, M)까지 미는 경우의 수를 구하면 된다.

단 파이프는 오른쪽, 아래쪽, 대각선으로만 밀 수 있고, 직각으로는(가로 -> 세로, 세로->가로) 밀 수 없다.

또한 파이프의 이동 경로는 반드시 빈 칸이어야 하고, 대각선의 경우에는 파이프가 놓여져 있는 총 4칸이 빈 칸이어야 한다.

2. 풀이

1) 알고리즘

    : DP

 

2) 풀이 설명

3차원 DP 벡터를 사용했다.

예를 들어 dp[x][y][z]는 x 방식(0 : 가로, 1 : 세로, 2 : 대각선)으로 (y, z)에 도달하는 경우의 수를 나타낸다.

2차원 DP를 사용하지 않고 이동 방식까지 나누어 3차 DP를 사용한 이유는 이동 방식(가로, 세로, 대각선)에 따라 다음에 이동할 수 있는 방식이 달라지기 때문이다.

 

모든 dp를 0으로 초기화해주고 가로 방식의 첫 행만 1로 초기화해주었다.

 

(i, j)로 이동하는 경우의 수는 다음과 같다.

1. (i, j-1)에서 가로 이동 (단 (i, j-1)에서는 세로로 도달했으면 안 된다.)

2. (i-1, j)에서 세로 이동 (단 (i-1, j)에서는 가로로 도달했으면 안 된다.)

3. (i-1, j-1)에서 대각선 이동

 

그러므로 점화식은 다음과 같이 세워진다.

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

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

dp[2][i][j] = dp[0][i-1][j-1] + dp[1][i-1][j-1] (단 좌하단, 우상단이 빈 공간일 때)

 

이렇게 다 돌고나면

dp[0][N][M] // 마지막에 가로 방식으로 (N, M)에 도달하는 경우의 수

+ dp[0][N][M] // 마지막에 세로 방식으로 (N, M)에 도달하는 경우의 수

+ dp[0][N][M] // 마지막에 대각선 방식으로 (N, M)에 도달하는 경우의 수

 

이 답이 된다.

 

3. 코드

더보기

 

/*
	파이프 옮기기 1
	알고리즘
*/
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	
	int N, answer = 0;
	cin >> N;

	vector<vector<int>> house(N+1, vector<int>(N+1));
	/*
		0 - 가로
		1 - 세로
		2 - 대각선
	*/
	vector<vector<vector<int>>> dp(3,vector<vector<int>>(N+1, vector<int>(N+1, 0)));

	for(int i=1;i<=N;i++)
		for(int j=1;j<=N;j++)
			cin >> house[i][j];

	for(int i=2;i<=N && !house[1][i];i++)
		dp[0][1][i] = 1;

	for(int i=2;i<=N;i++){
		for(int j=2;j<=N;j++){
			if(!house[i][j]){
				dp[0][i][j] = dp[0][i][j-1] + dp[2][i][j-1];
				dp[1][i][j] = dp[1][i-1][j] + dp[2][i-1][j];
				if(!house[i-1][j] && !house[i][j-1]){
					dp[2][i][j] = dp[0][i-1][j-1] + dp[1][i-1][j-1] + dp[2][i-1][j-1];
				}
			}
		}
	}
	for(int i=0;i<3;i++)
		answer += dp[i][N][N];
	
	cout << answer << endl;

	return 0;
}

 

4. 느낀점

 - 초기에 (1, 1), (1, 2)에 파이프가 놓여져있다는 걸 모르고 가로, 세로, 대각선 다 출발했다가 틀렸다. 역시 문제를 잘 읽어야 한다.

'알고리즘 > 백준' 카테고리의 다른 글

[백준/11501] 주식  (0) 2020.10.18
[백준/15312] 이름 궁합  (1) 2020.10.18
[백준/5557] 1학년  (1) 2020.10.18
[백준/1261] 알고스팟  (0) 2020.10.16
[백준/9655] 돌 게임  (0) 2020.10.16

1. 문제

1) 문제 링크

    : www.acmicpc.net/problem/5557

 

5557번: 1학년

상근이가 1학년 때, 덧셈, 뺄셈을 매우 좋아했다. 상근이는 숫자가 줄 지어있는 것을 보기만 하면, 마지막 두 숫자 사이에 '='을 넣고, 나머지 숫자 사이에는 '+' 또는 '-'를 넣어 등식을 만들며 놀��

www.acmicpc.net

 

2) 설명

숫자 배열이 있으면 마지막 숫자 전에 = 기호를, 나머지 각 숫자 사이에 +나 - 기호를 넣어서 등식을 성립하게 만들 수 있는 경우의 수를 구하면 된다.

단 계산 과정 중(왼쪽부터 계산하는 방식) 음수나 20을 초과하는 수가 나와서는 안 된다.

2. 풀이

1) 알고리즘

    : DP

 

2) 풀이 설명

 

0 ~ N-2 까지의 입력받은 숫자를 더하고 빼서 N-1번째에 입력받은 숫자를 만들어내면 된다.

즉 nums[0], nums[1], ... ,nums[N-2] 를 더하고 빼서 nums[N-1]을 만들면 된다.

 

그래서 i번째 이전 숫자들을 더하고 빼서 0부터 20까지 각각의 숫자를 만들 수 있는 경우의 수를 저장하는 DP 배열을 만들었다.

ex) DP[10][4] 에는 nums[0], nums[1], nums[2], nums[3], nums[4] 를 더하고 빼서 10을 만들 수 있는 경우의 수가 들어있다.

그러므로 DP배열은 21행( 0 ~ 20 ), N-1열( 0 ~ N-2 )을 가지게 된다.

초기값은 첫번째 DP[nums[0]][0] = 1로 주어진다.(첫 수(nums[0])으로 nums[0]을 만드는 경우의 수는 하나이기 때문에)

또한 점화식은 아래와 같이 세워진다.

 

DP[x][i] = DP[x][i] + DP[x-nums[i]] (단 x-nums[i] >= 0)

DP[x][i] = DP[x][i] + DP[x+nums[i]] (단 x+nums[i] <= 20)

 

이렇게 N-2열까지 돌리고나면 DP[nums[N-1]][N-2] 값이 답이 된다.(nums[0], nums[1], ... , nums[N-2]를 사용해서 nums[N-1]을 만드는 경우의 수)

 

3. 코드

더보기
/*
	1학년
	DP
*/
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	
	const int maxSum = 20;
	int N;
	cin >> N;

	vector<int> nums(N);

	for(int i=0;i<N;i++)
		cin >> nums[i];

	vector<vector<long long>> DP(21, vector<long long>(N, 0));
	
	DP[nums[0]][0] = 1;

	for(int i=1;i<N-1;i++){
		for(int j=0;j<=maxSum;j++){
			if(j-nums[i] >= 0)
				DP[j][i] += DP[j-nums[i]][i-1];
			if(j+nums[i] <= maxSum)
				DP[j][i] += DP[j+nums[i]][i-1];
		}
	}

	cout << DP[nums[N-1]][N-2] << "\n";

	return 0;
}

 

4. 느낀점

 

 - 값의 범위 잘 생각하기(long long 줘야하는데 int로 선언해서 틀렸었다.)

 - 가독성 높은 점화식 세우기

'알고리즘 > 백준' 카테고리의 다른 글

[백준/15312] 이름 궁합  (1) 2020.10.18
[백준/17070] 파이프 옮기기 1  (0) 2020.10.18
[백준/1261] 알고스팟  (0) 2020.10.16
[백준/9655] 돌 게임  (0) 2020.10.16
[백준/7569] 토마토  (0) 2020.10.10

1. 문제

1) 문제 링크

    : www.acmicpc.net/problem/1261

 

1261번: 알고스팟

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

www.acmicpc.net

 

2) 설명

0(빈 공간), 1(벽)로 이루어진 미로가 있고 (1, 1)에서 (N, M)으로 도달하면서 1(벽)을 최소한으로 뚫고 갈 때, 뚫고 간 벽의 수를 구하는 문제.

2. 풀이

1) 알고리즘

    : 우선순위 큐, BFS

 

2) 풀이 설명

  처음에는 DFS를 사용하면 되겠다 생각했지만 BFS와 우선순위 큐를 사용하는 게 편하다고 생각했다.

일반적인 BFS문제를 풀듯이 풀었으나 큐 대신 우선순위 큐에 튜플( - 뚫은 벽의 수, x 좌표, y 좌표)를 넣어줬다.

우선순위 큐는 맥스 힙 방식(가장 큰 값부터 빼는 방식)으로 동작하기 때문에 뚫은 벽의 수를 음수처리 해줌으로써 가장 적게 벽을 뚫은 노드부터 빼오도록 했다.

 

ex) ㄱ : (-3, 2, 4)와 ㄴ : (-10, 3, 5)가 있으면 ㄱ은 벽을 3번, ㄴ은 벽을 10번 뚫은 것이고, 우선순위 큐에서는 -3 > -10 이기 때문에 ㄱ을 먼저 뽑는다.

 

즉 해당 노드를 처음 방문한다면 그 방문은 벽을 최소로 뚫는 경로로 방문한 것이 된다.

그러므로 BFS를 돌던 중 (N, M) 좌표를 방문한다면 바로 while문을 탈출하고 답을 내면 된다.

 

3. 코드

더보기
/*
	알고스팟
    BFS, Priority queue
*/
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <tuple>

using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	
	int dx[4] = {-1, 1, 0, 0};
	int dy[4] = {0, 0, -1, 1};

	int N, M, answer = -1;
	cin >> M >> N;
	
	vector<vector<char>> miro(N+1, vector<char>(M+1));
	vector<vector<bool>> visited(N+1, vector<bool>(M+1, false));

	priority_queue<tuple<int, int, int>> pq;

	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++)
			cin >> miro[i][j];

	pq.push({0, 1, 1});
	visited[1][1] = true;

	while(!pq.empty()){
		int wall = get<0>(pq.top());
		int x = get<1>(pq.top());
		int y = get<2>(pq.top());
		pq.pop();
		
		if(x==N && y==M){
			answer = -wall;
			break;
		}
		for(int i=0;i<4;i++){
			int nx = x + dx[i];
			int ny = y + dy[i];

			if(nx<=0 || nx>N || ny<=0 || ny>M)
				continue;
			if(visited[nx][ny])
				continue;
			
			visited[nx][ny] = true;
			pq.push({(miro[nx][ny]=='1'? wall-1 : wall), nx, ny});
		}
	}
	
	cout << answer << endl;

	return 0;
}

 

 

4. 느낀점

 - 입력이 M, N 순서로 주어져서 일반적으로 생각하는 방식(N -> M)이랑 달라서 헷갈렸다.

 - visited를 안 써도 되지 않을까 잠시 생각했다. -> 무한루프 돌 수도 있다.

 - 인풋 한 줄을 한 글자씩 받아야 하는 걸 그냥 int로 받아버렸다. 입력 받기 신경쓰자.

 - 아직 tuple 사용법이 많이 미숙하다. 연습하자.

'알고리즘 > 백준' 카테고리의 다른 글

[백준/17070] 파이프 옮기기 1  (0) 2020.10.18
[백준/5557] 1학년  (1) 2020.10.18
[백준/9655] 돌 게임  (0) 2020.10.16
[백준/7569] 토마토  (0) 2020.10.10
[백준/2606] 바이러스  (0) 2020.10.10

1. 문제

1) 문제 링크

    : www.acmicpc.net/problem/9655

 

9655번: 돌 게임

상근이가 게임을 이기면 SK를, 창영이가 게임을 이기면 CY을 출력한다.

www.acmicpc.net

2) 설명

상근이와 창영이가 N개의 돌을 차례로 1개 혹은 3개씩 가져가면서 마지막 돌을 가져가는 사람이 이기는 게임을 한다.

상근이가 선공이고 둘 다 최선을 다할 때 누가 이기는지 출력하는 문제.(상근 SK, 창영 CY)

2. 풀이

1) 알고리즘

    : DP

 

2) 풀이 설명

  간단한 DP 문제.

N번째 돌을 반드시 집기 위해선 N-2번째 돌을 가져가면 된다.

내가 N-2번째 돌을 집으면 돌은 두 개가 남고 상대방은 1개, 3개의 돌만 가져갈 수 있기 때문에, 즉 남은 두 개 중 1개만 가져갈 수밖에 없기 때문에 상대가 돌 1개를 가져가고 마지막 N번째 돌을 가져오면 된다.

그러므로 초기값 A1 = 상근이가 이기는 경우, A2 = 창영이가 이기는 경우로 두고 N번까지 A_N = A_(N-2)을 돌리면 된다.

나는 bool 타입으로 dp값을 저장했다. 상근이가 이기는 경우 = true, 창영이가 이기는 경우 = false

 

3. 코드

더보기
/*
	돌 게임
	DP
*/
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	int N;
	cin >> N;
	
	vector<bool> dp(N+1);

	dp[1] = true;
	dp[2] = false;

	for(int i=3;i<=N;i++)
		dp[i] = dp[i-2];
		 
	cout << (dp[N]? "SK\n" : "CY\n");

	return 0;
}

 

 

4. 느낀점

 - 1차원 dp(점화식) 감잡기 좋은 문제라고 생각한다.

'알고리즘 > 백준' 카테고리의 다른 글

[백준/17070] 파이프 옮기기 1  (0) 2020.10.18
[백준/5557] 1학년  (1) 2020.10.18
[백준/1261] 알고스팟  (0) 2020.10.16
[백준/7569] 토마토  (0) 2020.10.10
[백준/2606] 바이러스  (0) 2020.10.10

1. 문제

1) 문제 링크

    : www.acmicpc.net/problem/7569

 

7569번: 토마토

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N과 쌓아올려지는 상자의 수를 나타내는 H가 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M ≤ 100, 2 ≤ N ≤ 100,

www.acmicpc.net

 

2) 설명

토마토가 다 익는 데 걸리는 시간(처음부터 모두 익어있었다면 0, 다 익을 수 없는 상황이면 -1)을 구하는 문제.

안 익은 토마토 = 0

토마토가 없는 칸 = -1

익은 토마토 = 1

로 주어지고 익은 토마토 주변의 안 익은 토마토들이 익는다.

 

 

2. 풀이

1) 알고리즘

   : BFS

 

2) 풀이 설명

예전에 풀었던 문제지만 다시 한번 풀어봤다.

 

전형적인 BFS문제다.

 

토마토 익을 때까지 큐에 넣다 뺐다 하면서 계속 돌리면 된다.

 

bfs에서 자주 사용하는 visited 배열(방문 확인을 위한)을 사용하지 않은 이유는 0인 칸을 방문하게 되면 (이전 칸 + 1) 값으로 계속 업데이트 해주기 때문에 각 칸이 0일 때만 다음에 방문할 수 있는 칸이 되기 때문이다. 

 

다 돌리고 box에 0(안 익은 토마토)가 남았다면 -1 리턴해주면 된다.

 

처음부터 다 익은 상황이었으면 answer 값이 1이 되므로 마지막에 출력하는 (answer - 1)이 0이 되어서, 모두 잘 익은 상황, 처음부터 모두 익은 상황을 커버할 수 있다.

 

3. 코드

더보기

 

/*
	토마토(3차원)
	BFS
	난이도 
*/

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <tuple>

using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	
	int answer = 0;
	int M, N, H;
	cin >> M >> N >> H;

	vector<vector<vector<int>>> box(H, vector<vector<int>>(N, vector<int>(M, 0)));
	queue<tuple<int, int, int>> q;
	int dx[6] = {-1, 1, 0, 0, 0, 0};
	int dy[6] = {0, 0, -1, 1, 0, 0};
	int dz[6] = {0, 0, 0, 0, -1, 1};
	
	//입력
	for(int i=0;i<H;i++){
		for(int j=0;j<N;j++){
			for(int k=0;k<M;k++){
				cin >> box[i][j][k];
				if(box[i][j][k] == 1)
					q.push({i, j, k});
			}
		}
	}
	//BFS 시작
	while(!q.empty()){
		int z = get<0>(q.front());
		int x = get<1>(q.front());
		int y = get<2>(q.front());
		q.pop();

		for(int i=0;i<6;i++){
			int nx = x + dx[i];
			int ny = y + dy[i];
			int nz = z + dz[i];

			if(nx<0 || nx>=N || ny<0 || ny>=M || nz<0 || nz>=H)
				continue;
			if(box[nz][nx][ny] != 0)
				continue;
			box[nz][nx][ny] = box[z][x][y] + 1;
			q.push({nz, nx, ny});
		}
	}

	//다 익을 수 없는 경우 체크, 토마토가 익는 데 걸리는 최대 시간 찾기
	for(int i=0;i<H;i++){
		for(int j=0;j<N;j++){
			for(int k=0;k<M;k++){	
				if(box[i][j][k] == 0){
					cout << "-1\n";
					return 0;
				}
				answer = max(answer, box[i][j][k]);
			}
		}
	}
	
	//1부터 셌으니까 -1 해서 출력
	cout << answer - 1 << "\n";

	return 0;
}

 

4. 느낀점

1) 잘 안 다뤄본 3차원 BFS를 하다 보니까 인덱스 설정이 헷갈려서 실수를 많이 했다.

2) 마지막에 안 익은 토마토가 있는지 확인하기 위한 루프를 돌아서 그런지 다른 풀이들보다 시간이 약간 더 걸린다.

   -> 변수가 많아지더라도 시간을 절약하는 것이 좋은지, 가시성과 명확성을 높이기 위해 각 프로세스를 구분하는 게 좋은지 고민해보야 할 것 같다.

3) 3차원 벡터 초기화와 함께 선언하는 게 많이 귀찮다. 더 편한 방법이 있는지 찾아보면 좋을 것 같다.

'알고리즘 > 백준' 카테고리의 다른 글

[백준/17070] 파이프 옮기기 1  (0) 2020.10.18
[백준/5557] 1학년  (1) 2020.10.18
[백준/1261] 알고스팟  (0) 2020.10.16
[백준/9655] 돌 게임  (0) 2020.10.16
[백준/2606] 바이러스  (0) 2020.10.10

+ Recent posts