반응형

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

 

9251번: LCS

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

www.acmicpc.net

 

 

💡 LCS

 

 

✅ 문제 풀이
  • 문자열에서 최장 부분 수열의 크기를 찾는 문제
  • DP를 이용하여 풀 수 있다.
  • dp 배열의 크기는 두 문자열 각각의 크기에 +1 한 만큼으로 행과 열 크기를 배정해준다.
  • 행 열 인덱스 0은 모두 0으로 초기화하여 사용할 것이다.
  • 그리고 dp 크기만큼 2중 for문을 순회하는데, 각 칸의 값은 해당 칸의 행과 열 알파벳이 동일하면 왼쪽 대각선 위 값에 +1 한 값이고, 다르면 왼쪽과 위에서 오는 값 중 더 큰 값으로 가지게 된다.
    => 이는 표를 그려보면 규칙을 통해 발견할 수 있다.
  • 따라서 이를 점화식으로 나타내면 다음과 같다.
    dp[i + 1][j + 1] = dp[i][j] + 1 (행 알파벳=열 알파벳)
    dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]) (행 알파벳 =/= 열 알파벳)

  • 그리고 dp[r][c] 즉, 가장 오른쪽 아래에 있는 값이 최종 답이 된다.

 

✏ 코드 전문
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>

using namespace std;

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	string str1, str2;

	cin >> str1 >> str2;

	int r = str1.length();
	int c = str2.length();

	vector<vector<int>> dp(r+1, vector<int>(c+1,0));
	
	for (int i = 0; i < r; i++) {
		for (int j = 0; j < c; j++) {
			int m = max(dp[i][j + 1], dp[i + 1][j]);
			if (str1[i] == str2[j]) {
				dp[i + 1][j + 1] = dp[i][j] + 1;
			}
			else {
				dp[i + 1][j + 1] = m;
			}
		}
	}

	cout << dp[r][c];
	
	return 0;
}
반응형
반응형

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

 

10026번: 적록색약

적록색약은 빨간색과 초록색의 차이를 거의 느끼지 못한다. 따라서, 적록색약인 사람이 보는 그림은 아닌 사람이 보는 그림과는 좀 다를 수 있다. 크기가 N×N인 그리드의 각 칸에 R(빨강), G(초록)

www.acmicpc.net

 

 

💡 적록색약

 

 

✅ 문제 풀이
  • 골드 치고 쉬운 dfs 문제
  • 보자마자 어떻게 풀어야 할지 접근방식이 바로 보이는 문제!!
  • 주의해야 할 점은 적록색약일 경우 R,G가 인접해있으면 같은 영역으로 본다는 점
  • 이런 영역 문제를 풀 때는 방문한 칸에 대해서는 방문 표시를 하기때문에, 두 가지 접근 방식에 대해서 서로 영향을 받지 않기 위해서 arr를 두개를 만들고 시작했다.
  • arr1은 적록색약이 아닌 사람이 보는 영역으로, R은 1, G는2, B는3 을 저장했다.
  • arr2는 적록색약인 사람이 보는 영역으로, R과 G는 1, B는 3을 저장했다. 이게 핵심이다. R과 G를 같은 값으로 배정하여 같은 영역으로 인식되도록 하였다.
  • 방문한 것에 대해서는 arr 값을 0으로 갱신하도록 하며, 방문 여부를 체크했다.
  • arr 크기만큼 순회하면서 각 칸이 0이 아니면 dfs 함수를 호출하여, 현재 위치와 현재 위치의 값, 그리고 참조할 arr를 인자로 넘겨주었다.
  • arr1과 arr2에 대해서 각각 취해야 하기 때문에 두번 씩 호출해 주었다.
  • dfs 함수 내 동작
  • 현재 칸을 방문했으므로 값을 0으로 갱신해준다.
  • 현재 칸을 기준으로 상하좌우가 arr의 범위내에 있고, 현재 칸과 값이 동일하면 같은 영역이므로 해당 칸에 대해서 DFS를 재귀호출하여 한 영역에 대해 계속 탐색하도록 하였다.
  • 다시 돌아와서, main의 2중 for문에서 dfs가 호출되었다는 것은 영역의 시작과 같은 의미이므로, dfs를 호출한 후에는 answer을 증가시켜 영역을 개수를 카운트 하였다.
  • 이때 적록색약이 아닌 경우는 answer1을, 적록색약인 경우는 answer2를 카운트 하도록 하였다.
  • 최종 answer들을 출력한다.

 

✏ 코드 전문
#include<iostream>
#include<vector>
#include<string>

using namespace std;

int n;
vector<vector<int>> arr1;
vector<vector<int>> arr2;

int answer1 = 0;
int answer2 = 0;

int dx[] = { -1,1,0,0 };
int dy[] = { 0,0,-1,1 };

void dfs(int i, int j, int num, vector<vector<int>> &arr) {
	arr[i][j] = 0;
	
	for (int k = 0; k < 4; k++) {
		int x = i + dx[k];
		int y = j + dy[k];
		if (x >= 0 && x < n&&y >= 0 && y < n&&arr[x][y] == num) {
			dfs(x, y, num, arr);
		}
	}
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> n;

	arr1.resize(n, vector<int>(n));
	arr2.resize(n, vector<int>(n));

	string str;
	for (int i = 0; i < n; i++) {
		cin >> str;
		for (int j = 0; j < n; j++) {
			if (str[j] == 'R') {
				arr1[i][j] = 1;
				arr2[i][j] = 1;
			}
			else if (str[j] == 'G') {
				arr1[i][j] = 2;
				arr2[i][j] = 1;
			}
			else {
				arr1[i][j] = 3;
				arr2[i][j] = 3;
			}
		}
	}

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (arr1[i][j] != 0) {
				dfs(i, j, arr1[i][j], arr1);
				answer1++;
			}
			if (arr2[i][j] != 0) {
				dfs(i, j, arr2[i][j], arr2);
				answer2++;
			}
		}
	}
	
	cout << answer1 << " " << answer2;

	return 0;
}
반응형
반응형

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

 

11729번: 하노이 탑 이동 순서

세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 순서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로

www.acmicpc.net

 

 

💡 하노이 탑 이동 순서

 

 

✅ 문제 풀이
  • 하노이 탑 문제는 재귀호출을 통해 풀이 할 수 있다.
  • 하노이 탑의 다음과 같은 공식 때문이다.
  • 탑 1에 있는 가장 작은 원판을 탑 3으로 옮긴다. 탑 3에 있는 가장 작은 원판을 탑 2로 옮긴다. 탑 2에 있는 가장 작은 원판을 탑 1로 옮긴다. 탑 1에 있는 가장 작은 원판을 탑 3으로 옮긴다.
  • k-1개의 원판은 위 공식을 따르게 된다. 
  • 따라서 각 원판을 재귀 호출을 통해 이동 순서를 표현할 수 있다.
  • 정리하자면, 1->3->2, 2->1->3 순서로 이동하게 된다.
  • 이를 위해 hanoi라는 함수를 선언해준다.
  • 이 함수는 현재 원판의 번호(=크기)와 시작(from), 중간(by), 끝(to) 지점을 인자로 갖는다.
    만약 원판의 번호가 1이면 "from to"을 출력하도록 한다.
    그렇지 않다면 hanoi를 호출하고 인자로 원판의 번호-1, from, to, by 순서로 넣어준다. 이는 1->3->2 순서를 나타낸 것이다.
    그리고 이 함수를 호출한 후에는 "from to"를 호출하여 이동 상황을 출력한다.
    그다음, hanoi를 한번 더 호출하여 인자로 원판의 번호-1, by, from, to 순서로 넣어준다. 이는 2->1->3 순서를 나타낸 것이다.
  • 따라서 원판 하나에 대해 hanoi 함수는 두번 재귀호출 하므로, 연산이 2^k번 발생하는데, 가장 큰 원판은 바로 이동할 수 있으므로 1을 빼준다. 따라서 총 이동 횟수는 2^k-1이 된다.
  • 참고로 c++의 경우, pow함수를 사용할 때 int로 형변환을 해주지 않으면 결과가 틀렸습니다가 나오므로, int로 꼭 변환한 후 -1을 빼주도록 한다. (pow함수의 반환 형태는 double 형이기 때문) 

 

✏ 코드 전문
#include<iostream>
#include<math.h>

using namespace std;

void hanoi(int n, int from, int by, int to) {
	if (n == 1) {
		cout << from << " " << to << "\n";
	}
	else {
		hanoi(n - 1, from, to, by);
		cout << from << " " << to << "\n";
		hanoi(n - 1, by, from, to);
	}
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int k;
	cin >> k;

	cout << int(pow(2, k)) - 1 << "\n";
	hanoi(k, 1, 2, 3);

	return 0;
}
반응형
반응형

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

 

7576번: 토마토

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토

www.acmicpc.net

 

 

💡 토마토

 

 

 

✅ 문제 풀이
  • BFS를 활용하는 문제이다.
  • queue를 사용하여 방문해야 할 곳을 pair<int,int> 형태의 좌표를 저장하도록 하였다.
  • 먼저 토마토에 대한 2차원 배열을 순회하면서 값이 1이면 그 위치를 q라는 queue에 pair<int,int> 형태로 넣는다.
  • 모두 탐색한 후에는 while문으로 q가 빌때까지 bfs를 수행한다.
  • q에서 가장 앞에 있는 front의 first와, second를 bfs의 인자로 넘겨준다.
  • BFS함수의 동작
    1. 인자로 받은 위치를 가지고, 동 서 남 북 방향에 있는 값이 배열의 인덱스 범위에 유효하며, 값이 0인지 확인한다.
    2. 위의 조건을 만족한다면 해당 위치의 arr값을 1로 변경해주고, temp라는 이름의 queue에 해당 위치를 pair<int,int> 형태로 넣어준다.
  • BFS함수를 돌고 나면 q에서 pop한다.
  • 그런다음, 현재 q가 비어있다면, 하루에 익을 수 있는 토마토는 다 익은 것이므로 하루를 카운트 하기 위해 answer를 1 증가시킨다.
  • 새로 익어진 토마토에 대한 정보를 가진 temp 있는 위치 정보들을 q에 다 옮겨놓는다.
  • 그리고나서 이어지는 다음 while문 동작부터는 다음 날짜에 대한 동작을 진행하는 것이다.
  • 그런데, q가 비어지게 되면 temp가 비어있고, 그날이 마지막 날이었더라도 하루가 무조건 더해지게 되기 때문에, 마지막에는 answer에서 1을 뺀 값이 답이된다.
  • while문을 모두 수행하고 나면, 갱신된 2차원 배열을 순회하면서, 하나라도 0이 있는 값이 있는지 찾아본다.
  • 이는 0이 하나라도 있다면 모든 토마토가 익어질 수 없다는 뜻이므로 -1을 출력하고 프로그램을 종료하기 위함이다.
  • 그렇지 않다면 answer-1을 출력하도록 하여, 토마토 판에서 토마토가 다 익는데 걸리는 날을 출력한다.

 

✏ 코드 전문
#include<iostream>
#include<vector>
#include<queue>

using namespace std;

int n, m;
vector<vector<int>> arr;
queue<pair<int, int>> q;
queue<pair<int, int>> temp;

int dx[] = { -1,1,0,0 };
int dy[] = { 0,0,-1,1 };
int answer = 0;

void bfs(int x, int y) {
	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&&arr[nx][ny] == 0) {
			arr[nx][ny] = 1;
			temp.push(pair<int, int>(nx, ny));
		}
	}
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> m >> n;

	arr.resize(n,vector<int>(m));

	bool end = true;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> arr[i][j];
			if (arr[i][j] == 1) {
				q.push(pair<int,int>(i, j));
			}
		}
	}

	while (!q.empty()) {
		bfs(q.front().first, q.front().second);
		q.pop();
		if (q.empty()) {//한번 돌았음을 의미
			answer++;
			while(!temp.empty()) {
				q.push(pair<int,int>(temp.front().first, temp.front().second));
				temp.pop();
			}
		}
	}

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (arr[i][j] == 0) {
				cout << -1;
				return 0;
			}
		}
	}

	cout << answer-1;

	return 0;
}
반응형
반응형

https://school.programmers.co.kr/learn/courses/30/lessons/144855

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

💡 문제
2022년 1월의 카테고리 별 도서 판매량을 합산하고, 카테고리(CATEGORY), 총 판매량(TOTAL_SALES) 리스트를 출력하는 SQL문을 작성해주세요.
결과는 카테고리명을 기준으로 오름차순 정렬해주세요.

 

 

✅ 문제 풀이
  • BOOK 테이블과 BOOK_SALES 테이블의 공통 속성인 BOOK_ID를 기준으로 INNER JOIN 해준다.
FROM BOOK A JOIN BOOK_SALES B ON A.BOOK_ID=B.BOOK_ID
  • 이 중에서 판매 날짜인 SALES_DATE가 2022-01-01과 2022-01-31 안에 있는 것만 WHERE 절로 추린다.
WHERE B.SALES_DATE BETWEEN '2022-01-01' AND '2022-01-31'
  • 카테고리 별로 묶어준다.
GROUP BY A.CATEGORY
  • 문제에서 CATEGORY 당 판매량 총합을 구하라고 했으니, CATEGORY 별로 SALES의 SUM을 구한다. 이름은 TOTAL_SALES로 출력한다.
SELECT A.CATEGORY, SUM(B.SALES) AS TOTAL_SALES
  • 조회결과를 CATEGORY에 대해 오름차순 정렬해준다.
ORDER BY CATEGORY;

 

 

✏ 코드 전문
SELECT A.CATEGORY, SUM(B.SALES) AS TOTAL_SALES
FROM BOOK A JOIN BOOK_SALES B ON A.BOOK_ID=B.BOOK_ID
WHERE B.SALES_DATE BETWEEN '2022-01-01' AND '2022-01-31'
GROUP BY A.CATEGORY
ORDER BY CATEGORY;
반응형
반응형

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

 

12865번: 평범한 배낭

첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)

www.acmicpc.net

 

 

💡 평범한 배낭
[문제]
이 문제는 아주 평범한 배낭에 관한 문제이다.한 달 후면 국가의 부름을 받게 되는 준서는 여행을 가려고 한다. 세상과의 단절을 슬퍼하며 최대한 즐기기 위한 여행이기 때문에, 가지고 다닐 배낭 또한 최대한 가치 있게 싸려고 한다.준서가 여행에 필요하다고 생각하는 N개의 물건이 있다. 각 물건은 무게 W와 가치 V를 가지는데, 해당 물건을 배낭에 넣어서 가면 준서가 V만큼 즐길 수 있다. 아직 행군을 해본 적이 없는 준서는 최대 K만큼의 무게만을 넣을 수 있는 배낭만 들고 다닐 수 있다. 준서가 최대한 즐거운 여행을 하기 위해 배낭에 넣을 수 있는 물건들의 가치의 최댓값을 알려주자.

[입력]
첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)가 주어진다.입력으로 주어지는 모든 수는 정수이다.

[출력]
한 줄에 배낭에 넣을 수 있는 물건들의 가치합의 최댓값을 출력한다.

 

 

✅ 문제 풀이
  • 배낭 문제는 전형적인 DP 문제
  • 배낭 문제 중에는 물건의 무게를 쪼갤 수 있는 fraction knapsack 문제와 쪼갤 수 없는 0-1 knapsack 문제로 구분할 수 있다.
  • 이 문제는 0-1 knapsack에 해당한다.
  • 1번 물건부터 n번 물건까지 각각의 물건을 넣을지 말지에 따라서 무게k 안에 담을 수 있는 최대 가치는 달라진다.
  • 이를 계산하기 위해 2차원 배열을 사용한다.
  • dp[i][j]에서 i는 i번째 물건까지 고려하면서 최대로 담을 수 있는 배낭의 무게가 j일때의 최대 가치를 갖는다.
  • 따라서 dp[i][j]를 구할 때는 i번 째 물건을 넣지 않은 경우와 넣은 경우 중 더 큰 가치를 반영하도록 한다.
    이는 i번째 물건의 무게가 j 이하여야 넣을 수 있기 때문에, 이를 꼭 확인한다.
    i번째 물건을 넣지 않는다면, i-1번째 물건까지 고려했을 때의 가치와 같다.
    i번째 물건을 넣는다면, i번째 물건의 가치+dp[i-1][j-i번째 물건의 무게]와 같다. (i번째 물건의 무게만큼 빼줘야 i를 넣음으로써 배낭의 무게가 j이하로 유지할 수 있기 때문이다)
    이 두 값 중 더 큰 값이 dp[i][j]가 된다.
  • 그런데 만약 i번째 물건의 무게가 j보다 크다면, 배낭에 넣을 수 없기 때문에, 이때의 dp[i][j]는 i-1번째 물건까지 고려했을 때의 가치와 동일하게 된다. 어차피 못넣기 때문이다.
  • 그렇다면 다음 두가지 경우에 대해서 각각의 점화식을 나타낼 수 있다.

🅰 j < i번째 물건의 무게

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

 

🅱 j >= i번째 물건의 무게

dp[i][j] = max(dp[i-1][j], v[i]+dp[i-1][j-w[i]])

 

  • 입력으로 받은 물건의 개수 n, 배낭에 담을 수 있는 최대 무게 k
  • 2중 for문을 통해 i는 1부터 n까지, j는 1부터 k까지 순회하며 dp[i][j]를 계산할 수 있다.
  • 이때 점화식에 의해서, 아직 2중 for문을 통해 계산되지 않은 범위의 dp값을 사용하게 되는 경우가 있는데, 이는 결국 조건을 충족하지 못해 0으로 계산되어야 한다. 따라서 처음에 2차원 배열 dp를 생성할 때, 크기가 (n+1) * (k+1)인 배열로 선언하고, 값은 모두 0으로 초기화 하여 시작하였다. 이렇게 하면 0 값을 가져다 쓸 수 있다. 
  • 2중 for문으로 dp를 모두 계산하고 나면, dp[n][k]에는 n번 물건까지 고려하여 배낭에 담을 수 있는 최대 무게가 k일때의 최대 가치가 담기게 된다.
  • 따라서 dp[n][k]를 출력해준다. 

 

 

✏ 코드 전문
#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int n, k;
	cin >> n >> k;

	vector<int> w(n+1,0);
	vector<int> v(n+1,0);
	
	for (int i = 1; i <= n; i++) {
		cin >> w[i] >> v[i];
	}

	vector<vector<int>> dp(n + 1, vector<int>(k + 1,0));
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= k; j++) {
			if (j < w[i]) {
				dp[i][j] = dp[i-1][j];
			}
			else {
				dp[i][j] = max(dp[i - 1][j], v[i] + dp[i - 1][j - w[i]]);
			}
		}
	}

	cout << dp[n][k];

	return 0;
}
반응형

+ Recent posts