191008 백준 알고리즘 문제풀기

|

[17143] 낚시왕

  • 난이도: 상
    • 시뮬레이션 문제로, 푸는데 총 2시간 소요됨..
      • 문제 풀이를 떠올리는데는 30분 미만이 소요되었지만 구현에 90분 이상 소요되었다.
      • 전체 틀을 짜는거는 30분도 안걸렸지만, 상어가 다음 위치로 움직이는 것을 처리하는데에서 엄청난 시간이 소요되었다.
    • 낚시왕 큰 문제를 작게 쪼개고, 순서대로 차근차근 접근해보니 이전보다 합리적인 방법으로 해결을 해메이지 않고 풀 수 있었다.
      • 입력을 받아 물고기 맵(구조체 활용)을 만들고
      • 낚시왕을 한칸 이동시키고
      • 조건에 맞게 물고기를 잡고
        • 여기서 탈출조건 확인
      • 상어를 이동시킨다.
    • 하지만 여럽지 않을것이라 생각했던 상어 이동 부분이 생각보다 복병이었다.
      • 별도의 테스트 코드와 테스트 케이스를 만들고 실험해보며 규칙성을 찾았다.
      • 노가다..
    • 전반적인 구상이 떠올랐을 때 미리 주석으로 계획을 짜 놓고 코딩하는게 훨씬 효율적이고 도움이 되었다.
  • 문제
    • https://www.acmicpc.net/problem/17143
    • 물고기 판 크기와 물고기 상태가 입력된다.
    • 낚시왕이 왼쪽끝에서 오른쪽 끝으로 가는동안 조건에 맞춰 잡을 수 있는 상어의 총 크기가 정답
      • 상어들은 매 초 속도 및 방향 조건에 따라 움직인다.
      • 낚시왕은 자기 바로 밑(같은 column)에 있는, 육지와 가장 가까운 상어 1마리만 잡을 수 있다.
      • 움직인 상어들은 가는 위치에 자신의 크기에 따라 위치하는 상어한테 잡아먹히거나 잡아먹을 수 있다.
  • 풀이
    • 우선, 입력을 받고
    • 낚시왕을 이동시키고
      • 만약 낚시왕이 탈출 조건이라면 프로그램을 끝내고
    • 이동된 위치에서 상어를 잡고
    • 상어를 이동시키는 순서대로 동작한다.
    • 자세한건 아래 코드에서 설명
#pragma warning(disable:4996)
#include <iostream>
#include <cstdio>
#include <vector>

using namespace std;

struct shark
{
	int s, d, z;
};//s: 상어 속도, d: 상어 방향, z: 상어 크기

struct zips
{
	int next, dir; // 다음 위치와 방향 저장용
};

struct position
{
	int nx, ny, dir; // 다음 좌표와 방향 저장용
};

shark sharks[102][102]; // 상어 저장용
shark temp[102][102]; // 상어 이동용

int dx[] = { 0,0,0,1,-1 }; // 각각 상어의 숫자로 매핑된 이동방향
int dy[] = { 0,-1,1,0,0 };

void temp_init(int w, int h)
{ // temp 배열을 0값으로 초기화한다.
	for (int j = 1; j < h + 1; j++)
	{
		for (int i = 1; i < w + 1; i++)
		{
			temp[j][i].s = 0;
			temp[j][i].d = 0;
			temp[j][i].z = 0;
		}
	}
}

void sharks_init(int w, int h)
{
	for (int j = 1; j < h + 1; j++)
	{
		for (int i = 1; i < w + 1; i++)
		{ // shakrs 배열을 temp(계산된 값)로 초기화한다.
			sharks[j][i].s = temp[j][i].s;
			sharks[j][i].d = temp[j][i].d;
			sharks[j][i].z = temp[j][i].z;
		}
	}
}


int flip(int cd)
{ // 상어 방향 뒤집기
	if (cd == 1) return 2;
	if (cd == 3) return 4;
	if (cd == 2) return 1;
	else return 3;
}
zips next(int min, int max, int next, int dir)
{ // 다음 위치를 정하는 함수 (핵심 함수!)
	zips answer; // 정답 크기와 방향을 리턴
	while (!(min <= next && next <= max))
	{ // 상어의 다음 위치가 상어 존재 가능 위치를 벗어나는동안 while문을 돈다.
		if (next > max)
		{ // 만약 오른쪽으로 벗어났다면
			int dist = next - max; // 벗어난 길이만큼
			next = max - dist; // 다음위치를 오른쪽에서 왼쪽으로 접어준다.
			dir = flip(dir); // 방향은 뒤집어준다.
			continue; // 넘어가기
		}
		else
		{ // 만약 왼쪽으로 벗어났다면
			int dist = min - next; // 벗어난 길이만큼
			next = dist + 1; // 오른쪽으로 접어준다. 인덱스 0이 끼어있으므로 1을 더한다.
			dir = flip(dir); // 방향은 뒤집어준다 .
		}
	}
	answer.next = next; // 다음 위치 값과
	answer.dir = dir; // 다음 방향 값을
	return answer; // 반환
}

position next_position(int cx, int cy, int cd, int speed, int w, int h)
{ // 다음 위치 정하기
	int min_x = 1; // 각각 미니멈/맥시멈 값을 정하고
	int min_y = 1;
	int max_x = w;
	int max_y = h;
	
	int nd = 0; // 다음 위치 및 방향을 초기화
	int nx = cx + dx[cd] * speed;
	int ny = cy + dy[cd] * speed;

	if (cd == 1)
	{ // 위쪽 보고 있는 상어
		zips temp;
		temp = next(min_y, max_y, ny, cd); // 다음 방향과 위치를 구함
		ny = temp.next;
		nd = temp.dir;
	}
	if (cd == 3)
	{ // 오른쪽 보고 있는 상어
		zips temp;
		temp = next(min_x, max_x, nx, cd); // 다음 방향과 위치를 구함
		nx = temp.next;
		nd = temp.dir;
	}
	if (cd == 2)
	{ // 아래 보고 있는 상어
		zips temp;
		temp = next(min_y, max_y, ny, cd); // 다음 방향과 위치를 구함
		ny = temp.next;
		nd = temp.dir;
	}
	if (cd == 4)
	{ // 왼쪽 보고 있는 상어
		zips temp;
		temp = next(min_x, max_x, nx, cd); // 다음 방향과 위치를 구함
		nx = temp.next;
		nd = temp.dir;
	}

	position result;
	result.nx = nx; // 다음 위치와 방향을 리턴
	result.ny = ny;
	result.dir = nd;

	return result;
}

int main()
{
	int H, W, M; // R: 세로길이, C: 가로길이
	scanf("%d %d %d", &H, &W, &M);
	int r, c, s, d, z;

	while (M--)
	{
		scanf("%d %d %d %d %d", &r, &c, &s, &d, &z);
		sharks[r][c].s = s; // 상어를 초기화
		sharks[r][c].d = d;
		sharks[r][c].z = z;
	}

	int fisher = 0; // 낚시꾼 초기 위치
	int answer = 0;
	while (1)
	{
		fisher++; // 낚시꾼 한 칸 이동하고
		if (fisher == W + 2)
			break; //끝점이라면 빠져나온다.

		//상어 잡기
		for (int j = 1; j < H + 2; j++)
		{
			if (sharks[j][fisher].z != 0)
			{//만약 가장 가까운 칸에 상어가 있다면
				//상어 잡는다.
				answer += sharks[j][fisher].z;
				sharks[j][fisher].d = 0;
				sharks[j][fisher].s = 0;
				sharks[j][fisher].z = 0;
				break; // 잡았으면 빠져나온다.
			}
		}
		//연산용  temp 초기화
		temp_init(W, H);
		//상어 이동
		for (int j = 1; j < H + 1; j++)
		{
			for (int i = 1; i < W + 1; i++)
			{
				if (sharks[j][i].z != 0)
				{//상어가 있다면
					int cx = i;
					int cy = j;
					int cd = sharks[j][i].d; // 현재 방향
					int cz = sharks[j][i].z; // 현재 크기
					int cs = sharks[j][i].s; // 현재 속도

					//다음 좌표 구한다.
					position npos = next_position(cx, cy, cd, cs, W, H);

					//temp에 자리 이동
					if (temp[npos.ny][npos.nx].z != 0)
					{//만약 temp에 상어 미리 존재한다면
						if (temp[npos.ny][npos.nx].z < cz)
						{ // 만약 temp 존재하는 상어 크기가 현재보다 작다면 덮어쓴다
							temp[npos.ny][npos.nx].d = npos.dir;
							temp[npos.ny][npos.nx].z = cz;
							temp[npos.ny][npos.nx].s = cs;
						}
					}
					else
					{ // temp의 자리에 상어가 없다면
						temp[npos.ny][npos.nx].d = npos.dir; // 연산된 방향과 현재 크기, 속도로 초기화
						temp[npos.ny][npos.nx].z = cz;
						temp[npos.ny][npos.nx].s = cs;
					}
				}
			}
		}
    //계산된 temp맵을 상어 맵으로 최신화
		sharks_init(W, H);
	}
	printf("%d\n", answer); // 결과 출력

	//system("pause");
	return 0;
}