본문 바로가기

알고리즘 문제 풀이 일지

백준 15807: *빛*영*우*

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

 

이 문제는 여러가지 풀이 방법이 있다. 누적 합을 이용하는 법, DP를 사용하는 법, 세그먼트 트리를 활용하는 법 등

여기서 나는 이 문제를 DP를 사용해서 풀었다. 하지만 사소한 실수로 너무 먼 길을 돌아왔다.

우선 풀이에 대해 이야기하자.

특정 위치에서 빛의 세기를 구할려면, 다음과 그림안에 몇 개의 라이트가 있는지 구하면 된다.

그럼 바로 위에 위치한 좌표의 빛의 세기를 구할려면 어떻게 해야할까?

파란 색으로 표시된 구역의 라이트 갯수를 구해야한다. 하지만, 일일히 해당 위치의 라이트 갯수를 세는 것은 시간초과가 날 것이 뻔하다. 그렇다면, 다른 곳에서 파란색 위치의 라이트 갯수를 다른 곳에 구할 수 없을까?

구할 수 있다. 빨간색은 왼쪽 아래의 그래프, 노란색은 오른쪽 아래의 그래프를 표시하였다.

따라서, (중심 라이트 여부) + (중심 바로 아래 라이트 여부) + (빨간색 영역) + (노란색 영역) - (빨노 겹치는 영역)

이렇게 정의할 수 있다. DP식을 정의하면

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

이렇게 구성할 수 있다.

#include <iostream>

using namespace std;

const int MAX = 3003;
int N, X, Y, P, G[MAX][MAX], S[MAX][MAX];

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	cin >> N;
	for (int i = 0; i < N; i++)
	{
		cin >> X >> Y;
		G[Y + 1502][X + 1502] = 1;
	}
	for (int i = 2; i < MAX; i++)
		for (int j = 2; j < MAX; j++)
			S[i][j] = S[i - 1][j - 1] + S[i - 1][j + 1] + G[i][j] + G[i - 1][j] - S[i - 2][j];
	cin >> P;
	while (P--)
	{
		cin >> X >> Y;
		cout << S[Y + 1502][X + 1502] << '\n';
	}
}

그런데, 이 코드는 정답이 아니였다. 이게 분명 정답이 틀림없다고 생각했던 나는 디버깅을 통해 배열의 값을 확인했다.

확인 결과, 왼쪽 끝 혹은 오른쪽 끝은 이 식을 만족하지 못한다. 그렇다면, 해당 경우의 수를 다시 고려해보자. 위의 식을 떠올렸기에, 끝의 점화식은 어렵지 않게 구할 수 있었다.

#include <iostream>

using namespace std;

const int MAX = 3005;
int N, X, Y, P, G[MAX][MAX], S[MAX][MAX];

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	cin >> N;
	for (int i = 0; i < N; i++)
	{
		cin >> X >> Y;
		G[Y + 1502][X + 1502] = 1;
	}
	for (int i = 2; i < MAX; i++)
		for (int j = 1; j < MAX; j++)
		{
			if (j == 1) S[i][j] = S[i - 1][j + 1] + G[i][j] + G[i - 1][j];
			else if (j == MAX - 1) S[i][j] = S[i - 1][j - 1] + G[i][j] + G[i - 1][j];
			else S[i][j] = S[i - 1][j - 1] + S[i - 1][j + 1] - S[i - 2][j] + G[i][j] + G[i - 1][j];
		}
	cin >> P;
	while (P--)
	{
		cin >> X >> Y;
		cout << S[Y + 1502][X + 1502] << '\n';
	}
}

그런데, 놀랍게도 이것도 정답이 아니였다.

자신의 풀이에 자신감이 없어진 나는 결국 구글링을 통해 답을 구하기로 결심했다.

https://measurezero.tistory.com/775

 

[BOJ 15807 // C++] *빛*영*우*

※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※ 이번에 볼 문제는 백준 15807번 문제인 *빛*영*우*이다. 문제는 아래 링크를 확인하자. https://www.acmicpc.ne

measurezero.tistory.com

누적합 테크닉 중 하나인, imos를 사용하면 답을 구할수 있다는 얘기를 듣고

https://driip.me/65d9b58c-bf02-44bf-8fba-54d394ed21e0

 

누적합의 확장, imos법

imos법에 대해 알아보자.

driip.me

imos 공부를 시작했다. 이 테크닉은

https://sumserv.tistory.com/134

 

백준 2283: 구간 자르기

https://www.acmicpc.net/problem/2283 이 문제는 풀이법이 재미있어서 다뤄보려한다.해결을 위해서 누적 합과 투 포인터란 다소 생소한 조합을 사용해야하는 것이 흥미로웠다. 큰 그림은 다음과 같다.왼

sumserv.tistory.com

이전에 사용했었던 기법인데, 이번에 새로 배운 건 1차원이 아닌 2차원에서 사용하는 방식이다.

이 방식을 어떻게 사용하면 이 문제를 해결할 수 있을지 고민을 하했다

 

맨 위의 블로그 글에 첨부된 코드를 따라치며, 로직을 이해려 노력하고, 자신만의 코드를 어떻게 구성할지 전전긍긍할 때 문득 코드의 한 문단에 눈이 갔다.

for (int i = 0; i < N; i++)
{
	cin >> X >> Y;
	G[Y + 1502][X + 1502] = 1;
}

참조한 코드는 여기서, 1로 초기화 하지 않고, 1을 더했다.

 

그러자, 내 초기 코드의 문제점을 발견했다. 아주 사소한 실수였다. 1을 대입하는 게 아니라 더했어야했다.

#include <iostream>

using namespace std;

const int MAX = 3005;
int N, X, Y, P, G[MAX][MAX], S[MAX][MAX];

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	cin >> N;
	for (int i = 0; i < N; i++)
	{
		cin >> X >> Y;
		G[Y + 1502][X + 1502]++; // 달라진 부분
	}
	for (int i = 2; i < MAX; i++)
		for (int j = 1; j < MAX; j++)
		{
			if (j == 1) S[i][j] = S[i - 1][j + 1] + G[i][j] + G[i - 1][j];
			else if (j == MAX - 1) S[i][j] = S[i - 1][j - 1] + G[i][j] + G[i - 1][j];
			else S[i][j] = S[i - 1][j - 1] + S[i - 1][j + 1] - S[i - 2][j] + G[i][j] + G[i - 1][j];
		}
	cin >> P;
	while (P--)
	{
		cin >> X >> Y;
		cout << S[Y + 1502][X + 1502] << '\n';
	}
}

사소한 실수 하나 때문에, 공부까지하게 된 어처구니 없는 경험이였다..

좀 더 내 코드에 자신감을 가지도록 하자.

'알고리즘 문제 풀이 일지' 카테고리의 다른 글

백준 1044: 팀 선발  (0) 2024.12.03
백준 2258: 정육점  (0) 2024.11.29
백준 5624: 좋은 수  (0) 2024.11.22
백준 2024: 선분 덮기  (0) 2024.11.19
백준 2923: 숫자 게임  (0) 2024.11.16