반응형

문제링크:www.acmicpc.net/problem/14267

 

14267번: 내리 칭찬

영선회사에는 매우 좋은 문화가 있는데, 바로 상사가 직속 부하를 칭찬하면 그 부하가 부하의 직속 부하를 연쇄적으로 칭찬하는 내리 칭찬이 있다. 즉, 상사가 한 직속 부하를 칭찬하면 그 부하

www.acmicpc.net

dfs 문제였습니다.

이때 완전탐색 횟수를 줄이는게 핵심인 문제였습니다.

 

우선 첫번째 코드를 먼저 보시죠.

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

int n = 0, m = 0;
int cost[100001] = { 0, };
vector <int> vec[100001];
int h, w;

void dfs(int current) {

	cost[current] += w;
	for (int i = 0; i < vec[current].size(); i++) {
		dfs(vec[current][i]);
	}
}

int main() {

	ios_base::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		int temp;
		cin >> temp;
		if (i == 1) continue;
		vec[temp].push_back(i);
	}

		
	for (int i = 1; i <= m; i++) {
		cin >> h >> w;
		dfs(h);
	}

	for (int i = 1; i <= n; i++) {
		cout << cost[i];
		if (i == n)
			break;
		cout << ' ';
	}
	cout << '\n';

	return 0;
}

 

일반적으로 생각할 수 있는 방법입니다. 

근데 이렇게 제출하면 당연히 시간 초과가 납니다. 

 

좀더 효율적으로 탐색할 수 없을까 고민을 하던중에 

dfs를 한번만 돌아도 되는 아이디어를 생각했습니다.

아래처럼 dfs(1)을 호출하고 줄줄이 이전 cost값을 더해주는 형태로 구현했습니다. 

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

int n = 0, m = 0;
int cost[100001] = { 0, };
vector <int> vec[100001];
int h, w;

void dfs(int current) {

	for (int i = 0; i < vec[current].size(); i++) {
		cost[vec[current][i]] += cost[current];
		dfs(vec[current][i]);
	}
}

int main() {

	ios_base::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		int temp;
		cin >> temp;
		if (i == 1) continue;
		vec[temp].push_back(i);
	}

		
	for (int i = 1; i <= m; i++) {
		cin >> h >> w;
		cost[h] += w;
	}
	dfs(1);
	for (int i = 1; i <= n; i++) {
		cout << cost[i];
		if (i == n)
			break;
		cout << ' ';
	}
	cout << '\n';

	return 0;
}
반응형

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

[백준] 15686-치킨 배달(C++)  (0) 2021.01.11
[백준] 2023-신기한 소수(C++)  (0) 2021.01.02
[백준] 2580-스도쿠(C++)  (0) 2020.08.19
[백준] 9663-N-Queen(C++)  (0) 2020.07.27
[백준] 2573-빙산(C++)  (0) 2020.06.09
반응형

문제링크:www.acmicpc.net/problem/1301

 

1301번: 비즈 공예

첫째 줄에 다솜이가 가지고 있는 구슬의 종류 N이 주어진다. N은 3보다 크거나 같고, 5보다 작거나 같다. 둘째 줄부터 N개의 줄에 각각의 구슬이 총 몇 개 있는 지주어진다. 첫째 줄에는 1번 구슬,

www.acmicpc.net

 

문제의 핵심 조건은 N은 3보다 크거나 같고, 5보다 작거나 같다. 둘째 줄부터 N개의 줄에 각각의 구슬이 총 몇 개 있는 지주어진다. 첫째 줄에는 1번 구슬, 둘째 줄에는 2번 구슬, 이런 형식이다. 각각의 구슬은 10개보다 작거나 같은 자연수이다. 그리고, 구슬의 총 개수의 합은 35를 넘지 않는다.

 

수의 범위가 작기때문에 긴 차원의 배열을 만들어도 된다는 것을 알게되었습니다.

모든 경우를 고려해주는 것으로 생각을 가지게 되었습니다.

 

 무려 7차원 배열입니다.

dp[dp[arr[1]][arr[2]][arr[3]][arr[4]][arr[5]][prefer][current] = 5종류의 구슬이 arr[i] 개씩 남아있고, 이전 구슬이 prefer, 현재 선택한 구슬이 current일때의 경우의 수의 합입니다.

 

 

그렇다면 재귀식은 아래 처럼 구현할 수 있습니다.

long long solved(int prefer, int current,int cnt) {
	if (cnt == sum) {
		return 1;
	}
	
	long long &ret = dp[arr[1]][arr[2]][arr[3]][arr[4]][arr[5]][prefer][current];
	if (ret != -1)
		return ret;
	ret = 0;
	for (int i = 1; i <= n; i++) {
		if (prefer != i && current != i && arr[i]) {
			arr[i]--;
			ret += solved(current, i, cnt+1);
			arr[i]++;
		}

	}

	return ret;
}

 

 

아래는 전체 정답 코드입니다.

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

int n = 0, result = 0,sum=0;
int arr[6] = { 0, };
long long dp[11][11][11][11][11][6][6] = { 0, };

long long solved(int prefer, int current,int cnt) {
	if (cnt == sum) {
		return 1;
	}
	
	long long &ret = dp[arr[1]][arr[2]][arr[3]][arr[4]][arr[5]][prefer][current];
	if (ret != -1)
		return ret;
	ret = 0;
	for (int i = 1; i <= n; i++) {
		if (prefer != i && current != i && arr[i]) {
			arr[i]--;
			ret += solved(current, i, cnt+1);
			arr[i]++;
		}

	}

	return ret;
}

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> arr[i];
		sum += arr[i];
	}
	memset(dp, -1, sizeof(dp));
	cout << solved(0, 0, 0) << endl;

	return 0;
}
반응형
반응형

문제링크:www.acmicpc.net/problem/2411

 

2411번: 아이템 먹기

첫째 줄에 N, M(1≤N, M≤100), A(1≤A), B(0≤B)가 주어진다. A는 아이템의 개수이고, B는 장애물의 개수이다. 다음 A개의 줄에는 아이템의 위치, B개의 줄에는 장애물의 위치가 주어진다. 위치를 나타낼

www.acmicpc.net

 

먼가 구현과 dp 중간느낌의 문제였습니다.

 

장애물을 피하면서 아이템을 모두 들렀다가 최종지점인 n,m에 도착하는 경우에 수 입니다.

이때 n,m은 위, 오른쪽으로만 이동이 가능하기 때문에, 쉬운 문제였습니다. 

 

  • 1. 모든 아이템 좌표 저장하기 (저장후 정렬을 통해서 이동 순서 정해주기)
  • 2. 이동 순서대로 이동하면서 dp가 채워주기 

 dp[i][j]는 기본적으로 dp[i-1][j] + dp[i][j-1]입니다.

만약 이전에 장애물이었다면 dp[i-1][j] 이나 dp[i][j-1]하나만

더해주고, 둘다 장애물이었다면 그대로 0입니다. 

 

아래는 정답코드입니다.

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

typedef struct MyStruct
{
	int y;
	int x;
}game;

vector <game> item;
int arr[102][102] = { 0, };
int dp[102][102] = { 0, };
int n = 0, m = 0, a, b;

bool cmp(game a, game b) {
	if (a.x < b.x) return 1;
	else if (a.x == b.x) {
		if (a.y < b.y) return 1;
	}
	return 0;
}

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

	cin >> n >> m >> a >> b;
	int temp_y,temp_x;
	for (int i = 1; i <= a; i++) {
		cin >> temp_y >> temp_x;
		item.push_back({ temp_y,temp_x });
		arr[temp_y][temp_x] = 1;
	}
	item.push_back({ n,m });
	for (int i = 1; i <= b; i++) {
		cin >> temp_y >> temp_x;
		arr[temp_y][temp_x] = -1;
	}
	/*
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++)
			cout << arr[i][j] << ' ';
		cout << endl;
	}
	*/
	dp[1][0] = 1;
	sort(item.begin(), item.end(), cmp);

	int current_y = 1, current_x = 1;

	for (int cnt = 0; cnt < item.size(); cnt++) {
		int des_y = item[cnt].y;
		int des_x = item[cnt].x;

		for (int i = current_y; i <= des_y; i++) {
			for (int j = current_x; j <= des_x; j++) {
				if (arr[i][j] == -1)
					dp[i][j] = 0;
				else if (arr[i - 1][j] != -1 && arr[i][j - 1] != -1) {
					dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
				}
				else if (arr[i - 1][j] != -1) {
					dp[i][j] = dp[i - 1][j];
				}
				else if (arr[i][j - 1] != -1)
					dp[i][j] = dp[i][j - 1];
			}
		}

		current_y = des_y;
		current_x = des_x;
	}
	/*
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++)
			cout << arr[i][j] << ' ';
		cout << endl;
	}
	cout << endl;

	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++)
			cout << dp[i][j] << ' ';
		cout << endl;
	}*/

	cout << dp[n][m] << endl;
	return 0;
}
반응형
반응형

문제링크:www.acmicpc.net/problem/11054

 

11054번: 가장 긴 바이토닉 부분 수열

첫째 줄에 수열 A의 크기 N이 주어지고, 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ N ≤ 1,000, 1 ≤ Ai ≤ 1,000)

www.acmicpc.net

골드 3의 난이도는 아니였습니다. 다만 아이디어를 생각하는데 조금 어려움을 겪었습니다.

경우는 총 3가지 입니다.

 

올라갔다 다시 내려가는 경우 -> {10, 20, 30, 25, 20}

오름차순 ->{10, 20, 30, 40}

내림차순 ->{50, 40, 25, 10}

 

오름차순과 내림차순의 dp는 O(n^2)으로 쉽게 구할수 있습니다.

각각을 dp[i]와 dp_reverse[i]라고 하겠습니다.

 

올라갔다 내려가는 경우는 

dp[i] + dp_reverse[i] - 1 입니다. 해당 i에서의 오름차순 값 + 내림차순 값 - 1입니다. -1을 하는 이유는 자기자신이 두번 더해졌기 때문입니다. 

 

 

아래는 전체 정답코드입니다.

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

int dp[1002];
int dp_reverse[1002];
int n = 0;
int arr[1001] = { 0, };
int result = 1;

void solve_forward() {

	for (int i = 2; i <= n; i++) {

		for (int j = i - 1; j >= 1; j--) {
			if (arr[j] < arr[i])
				if (dp[i] < dp[j] + 1)
					dp[i] = dp[j] + 1;

		}
	}
}
void solve_backward() {
	for (int i = n - 1; i >= 1; i--) {
		for (int j = i + 1; j <= n; j++) {
			if (arr[j] < arr[i])
				if (dp_reverse[i] < dp_reverse[j] + 1)
					dp_reverse[i] = dp_reverse[j] + 1;
		}

	}
}

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> arr[i];
		dp[i] = 1;
		dp_reverse[i] = 1;
	}
	
	solve_forward();
	solve_backward();
	
	for (int i = 1; i <= n; i++) {

		if (dp[i] + dp_reverse[i] - 1 > result)
			result = dp[i] + dp_reverse[i] - 1;
		
		if (dp[i] > result)
			result = dp[i];
		else if (dp_reverse[i] > result)
			result = dp_reverse[i];
		
	}
	cout << result << endl;
	return 0;

}

 

 

반응형

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

[백준] 1301-비즈 공예(C++)  (0) 2021.01.01
[백준] 2411-아이템 먹기(C++)  (0) 2020.12.31
[백준] 1937-욕심쟁이 판다(C++)  (0) 2020.12.29
[백준] 1520-내리막 길(C++)  (0) 2020.12.28
[백준] 2629-양팔저울(C++)  (0) 2020.12.24
반응형

문제링크:www.acmicpc.net/problem/1937

 

1937번: 욕심쟁이 판다

n*n의 크기의 대나무 숲이 있다. 욕심쟁이 판다는 어떤 지역에서 대나무를 먹기 시작한다. 그리고 그 곳의 대나무를 다 먹어 치우면 상, 하, 좌, 우 중 한 곳으로 이동을 한다. 그리고 또 그곳에서

www.acmicpc.net

내리막길 문제와 유사해서 크게 어렵지는 않았습니다.

예를 들어서, 단순히 오른쪽, 아래로만 이동하는게 아니라 위에 올라갔다가, 왼쪽으로 갔다가 올 수도 있기 때문에

모든경우를 탐색해주어야하는 이때, dp의 메모이제이션을 사용하는 식입니다.

dp의 UP-DOWN 방식으로 구현을 하자는 아이디어를 얻었습니다.

 

점화식은 아래처럼 구현했습니다. 이때 ny, nx는 인접한 4방향 좌표를 모두 탐색합니다.

if (ny >= 1 && ny <= n&&nx >= 1 && nx <= n)
			if (arr[y][x] < arr[ny][nx]) {
				ret = max(ret, solved(ny, nx) + 1);
		}

 

고민했던 부분은 모든 좌표에서 재귀함수를 호출하면 시간이 오래걸리지 않을까? 입니다.

근데 이때 dp는 초기화할 필요가 없기때문에 시간적으로 오래걸리지 않습니다.

dp[i][j]는 i,j 좌표에서 이동할 수 있는 최대값을 의미합니다.

 

 

아래는 전체 정답코드입니다.

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

int n = 0;
int arr[501][501] = { 0, };
int dp[501][501];
int y_ar[4] = { 0,0,1,-1 };
int x_ar[4] = { 1,-1,0,0 };
int result = 0;

int solved(int y, int x) {
	int &ret = dp[y][x];
	if (ret != -1) return ret;
	ret = 0;

	for (int i = 0; i < 4; i++) {
		int ny = y + y_ar[i];
		int nx = x + x_ar[i];
		if (ny >= 1 && ny <= n&&nx >= 1 && nx <= n)
			if (arr[y][x] < arr[ny][nx]) {
				ret = max(ret, solved(ny, nx) + 1);
		}

	}
	return ret;
}

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

	cin >> n;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			cin >> arr[i][j];
	memset(dp, -1, sizeof(dp));
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) {
				result = max(result, solved(i, j));
			
		}

	cout << result + 1 << endl;
	return 0;
}

dp라고 인식되는  순간 체감난이도가 확 줄었습니다.

이해가 안되시면 천천히 디버깅해보세요!

 

반응형
반응형

문제링크:www.acmicpc.net/problem/1520

 

1520번: 내리막 길

첫째 줄에는 지도의 세로의 크기 M과 가로의 크기 N이 빈칸을 사이에 두고 주어진다. 이어 다음 M개 줄에 걸쳐 한 줄에 N개씩 위에서부터 차례로 각 지점의 높이가 빈 칸을 사이에 두고 주어진다.

www.acmicpc.net

 

dp라는걸 모르고 풀었음 더 어려웠을 문제였습니다. 
처음에 bfs 관점으로 접근하는데 자꾸 예외케이스가 생겼습니다.

예를 들어서, 단순히 오른쪽, 아래로만 이동하는게 아니라 위에 올라갔다가, 왼쪽으로 갔다가 올 수도 있기 때문입니다.

 
그래서 모든 경우를 탐색해주어야 한다고 인식하게 되었고, dp의 UP-DOWN 방식으로 구현을 하자는 아이디어를 얻었습니다.

 

점화식은 아래처럼 구현했습니다. 이때 ny, nx는 인접한 4방향 좌표를 모두 탐색합니다.

if (ny >= 1 && ny <= m && nx >= 1 && nx <= n) {
			if (arr[y][x] < arr[ny][nx]) {
				ret += solved(ny, nx);
			}
		}

 

 

 

아래는 전체 정답코드입니다.

#include <iostream>
#include <cstring>
using namespace std;
/*
	dp라고 모르고 풀었음 더 어려웠을 문제
	처음에 bfs 관점으로 접근하는데 자꾸 예외케이스가 생김
	모든 경우를 탐색해주어야 한다고 인식하게 됨 -> UP-DOWN 방식으로 구현
*/
int m, n;
int arr[501][501] = { 0, };
int dp[501][501] = { 0, };

int y_ar[4] = {0, 0, -1, 1};
int x_ar[4] = { 1,-1,0,0 };

int solved(int y, int x) {
	if (y == 1 && x == 1)
		return 1;
	int &ret = dp[y][x];
	if (ret != -1) return ret;
	ret = 0;

	for (int i = 0; i < 4; i++) {
		int ny = y + y_ar[i];
		int nx = x + x_ar[i];
		if (ny >= 1 && ny <= m && nx >= 1 && nx <= n) {
			if (arr[y][x] < arr[ny][nx]) {
				ret += solved(ny, nx);
			}
		}
	}

	return ret;
}

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

	cin >> m >> n;

	for (int i = 1; i <= m; i++)
		for (int j = 1; j <= n; j++)
			cin >> arr[i][j];

	memset(dp, -1, sizeof(dp));
	cout << solved(m,n) << endl;

	/*
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= n; j++)
			cout << dp[i][j] << ' ';
		cout << endl;
	}
	*/
	return 0;
}

dp라고 인식되는  순간 체감난이도가 확 줄었습니다.

이해가 안되시면 천천히 디버깅해보세요!

 

반응형
반응형

2020 하반기 UIT 개발직무 인턴 면접후기입니다.

이전 pretest 이후 진행되는 전형이었습니다.

gusdnr69.tistory.com/category/%ED%9B%84%EA%B8%B0

 

'후기' 카테고리의 글 목록

코딩, 개발, 애니덕질

gusdnr69.tistory.com

 

3개문제 90분짜리 코딩테스트를 먼저 보고 이후 20분짜리 화상 면접이 진행되었습니다. 

문제난이도는 백준 solve.ac 기준 실버상위 정도의 난이도로 코딩테스트 난이도는 매우 낮았습니다.

문제가 너무 쉬워서 pretest 통과하신분들은 걱정할 필요 없으실것 같았습니다. 그냥 코딩테스트 부정행위 방지하려고 같은 난이도로 한번더 시험 본 느낌이었습니다.

 

 

그리고 이렇게 제출마다 정답여부를 알려주는게 특징입니다.

 

평소 코딩테스트 준비를 성실하게 하셨다면 전혀 어려움 느끼지 않을겁니다.

 

 

 

 

 

면접은 1:1면접으로 진행했습니다. 임원은 아니고 실제 현업 개발자 분께서 진행해주셨습니다. 직무에 관한 기본적인 질문들 물으셨고, 분위기는 아주 편안하고 따뜻한 느낌이었습니다. 

근데 저는 준비가 많이 부족했던 터라 질문에 많이 답변을 못했습니다. ㅠㅠ

그러다 보니 질문을 많이 받지도 못하고 12분정도만에 마무리 되었습니다.. ㅠㅠ

저는 떨어질것 같습니다. ( 마지막에 면접관:  "좋은경험 되셨으리라 생각하고 이만 면접 마치겠습니다. " 라고 하셨습니다..ㅠㅠ)

꼭 준비 많이하시고 임하시면 좋을 것 같습니다. 

 

 

----추가-----

 

 

근데 붙었습니다..ㅋㅋㅋㅋㅋ

퍼블리셔 직무라서 고민됩니다. ㅠㅠ 

아마 코테 결과가 좋아서 합격한거 같습니다. 

합격하신분들도 대부분 코테를 다 맞으셨더라구요.

화이팅

반응형
반응형

문제링크:www.acmicpc.net/problem/12904

 

12904번: A와 B

수빈이는 A와 B로만 이루어진 영어 단어가 존재한다는 사실에 놀랐다. 대표적인 예로 AB (Abdominal의 약자), BAA (양의 울음 소리), AA (용암의 종류), ABBA (스웨덴 팝 그룹)이 있다. 이런 사실에 놀란 수

www.acmicpc.net

 

(1 ≤ S의 길이 ≤ 999, 2 ≤ T의 길이 ≤ 1000, S의 길이 < T의 길이)

문자열의 범위가 위와 같으므로 재귀함수를 사용해 구현하면 시간 초과입니다.

 

S를 통해서 T를 만드는데 이때 T를 통해서 S를 유추하면 더욱 쉽다는 것을 아는지 모르는지에 따라서 난이도가 크게 달라지는 문제입니다.

 

T에서 두가지 규칙에 의해서 S문자열의 길이와 같아지게 될때 까지 반복하면 S를 통해서 T를 만들 수 있는지 여부가 결정되기 때문입니다.

 

구현자체의 난이도는 하,  문제 해결 아이디어가 중상..

 

아래는 정답코드입니다. 코드로 보면 충분히 이해가 되실겁니다.

 

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


string s, t;
bool result = 0;


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

	cin >> s;
	cin >> t;


	while (1) {

		if (s.size() == t.size()) {
			if (s == t)
				result = 1;
			break;
		}
		
		if (t[t.size() - 1] == 'A')
			t.pop_back();
		else {
			t.pop_back();
			reverse(t.begin(), t.end());
		}
	}

	cout << result << endl;
}
반응형

'알고리즘 > 구현' 카테고리의 다른 글

[백준] 2529-부등호(C++)  (0) 2021.01.20
[백준] 2839-설탕 배달(C++)  (0) 2020.12.21
[백준] 2116-주사위 쌓기(C++)  (0) 2020.12.18
[백준] 2636-치즈(C++)  (0) 2020.12.18
[백준] 14719-빗물(C++)  (0) 2020.12.15

+ Recent posts