[프로그래머스] 당구 연습

치즈오믈렛·2023년 4월 2일
0

코딩테스트 연습

목록 보기
7/7

1. 첫 번째 접근

  1. 두개의 공이 벽까지의 거리가 가장 짧은 벽을 선택
  2. 예외처리
    • 가는 길에 공이 있을 경우(벽에 부딪치기 전에 공에 막힘) 다른 벽을 쿠션으로 사용한다.
  3. 두 공들의 벽까지 수직거리 구함
  4. 벽에 투영했을 때 공사이의 거리
  5. 투영한 거리를 수직거리의 비율로 나눔
  6. 피타고라스 정리로 거리 구함
public int[] solution(int m, int n, int startX, int startY, int[,] balls)
{
	int[] answer = new int[] { };

	int startXToLeft = startX;
	int startXToRight = m  - startX;
	int startYToDown = startY;
	int startYToUp = n  - startY;

	List<int> ans= new List<int>();
	for (int i = 0; i < balls.GetLength(0); i++)
	{
		int destX = balls[i, 0];
		int destY = balls[i, 1];

		int destXToLeft = destX;
		int destXToRight = m - destX;
		int destYToDown = destY;
		int destYToUp = n - destY;

		bool isLeft;
		bool isDown;

		isLeft = startXToLeft + destXToLeft < startXToRight + destXToRight;
		isDown = startYToDown + destYToDown < startYToUp + destYToUp;

		int minX = Math.Min(startXToLeft + destXToLeft, startXToRight + destXToRight);
		int minY = Math.Min(startYToDown + destYToDown, startYToUp + destYToUp);

		int startHeight;
		int destHeight;
		int startWidth;
		int destWidth;
		// 1. 두개의 공이 벽까지의 거리가 가장 짧은 벽을 선택
		if (minX < minY)
		{
			// 3. 두 공들의 벽까지 수직거리 구함
			startHeight = isLeft ? startXToLeft : startXToRight;
			destHeight = isLeft ? destXToLeft : destXToRight;
			// 4. 벽에 투영했을 때 공사이의 거리
			// 5. 투영한 거리를 수직거리의 비율로 나눔
			startWidth = Math.Abs(startY - destY) * startHeight / (startHeight + destHeight);
			destWidth = Math.Abs(startY - destY) * destHeight / (startHeight + destHeight);
		}
		else
		{
			startHeight = isDown ? startYToDown : startYToUp;
			destHeight = isDown ? destYToDown : destYToUp;
			startWidth = Math.Abs(startX - destX) * startHeight / (startHeight + destHeight);
			destWidth = Math.Abs(startX - destX) * destHeight / (startHeight + destHeight);
		}
		// 예외처리 
		if (destX == startX)
		{
			startHeight = isLeft ? startXToLeft : startXToRight;
			destHeight = isLeft ? destXToLeft : destXToRight;
			startWidth = Math.Abs(startY - destY) * startHeight / (startHeight + destHeight);
			destWidth = Math.Abs(startY - destY) * destHeight / (startHeight + destHeight);
		}
		else if (destY == startY)
		{
			// y값을 높이로 사용하는 삼각형을 만든다.
			startHeight = isDown ? startYToDown : startYToUp;
			destHeight = isDown ? destYToDown : destYToUp;
			startWidth = Math.Abs(startX - destX) * startHeight / (startHeight + destHeight);
			destWidth = Math.Abs(startX - destX) * destHeight / (startHeight + destHeight);
		}

		// 6. 피타고라스 정리로 거리 구함
		double startToWall = Math.Sqrt(Math.Pow(startHeight, 2) + Math.Pow(startWidth, 2));
		double destToWall = Math.Sqrt(Math.Pow(destHeight, 2) + Math.Pow(destWidth, 2));
		double distance = startToWall + destToWall;
		ans.Add((int)Math.Pow(distance,2));
	}
	answer = ans.ToArray();
	return answer;
}

결과 : 실패

2. 두 번째 접근

  • 그림을 다시 보니 맞은 벽을 기준으로 이동한 길을 반전시키면 튕겨나온 길이 직선이 된다.
  • 이렇게 해결하면 소수점을 계산할 필요도 없어진다.
  1. 가장 가까운 벽을 찾는다.
  2. 두 공이 벽까지의 거리를 합한다.
  3. 두 공을 벽에 투영했을때의 거리를 구한다.
  4. 피타고라스
public int[] solution(int m, int n, int startX, int startY, int[,] balls)
{
	int[] answer = new int[] { };

	int startXToLeft = startX;
	int startXToRight = m  - startX;
	int startYToDown = startY;
	int startYToUp = n  - startY;

	List<int> ansList= new List<int>();
	for (int i = 0; i < balls.GetLength(0); i++)
	{
		int destX = balls[i, 0];
		int destY = balls[i, 1];

		int destXToLeft = destX;
		int destXToRight = m - destX;
		int destYToDown = destY;
		int destYToUp = n - destY;

		int minX = Math.Min(startXToLeft + destXToLeft, startXToRight + destXToRight);
		int minY = Math.Min(startYToDown + destYToDown, startYToUp + destYToUp);

		int height;
		int width;
		int ans;
        
		// 예외처리 
		if (destX == startX)
		{
			height = minX;
			width = Math.Abs(startY - destY);
			ans = (int)Math.Pow(height,2) + (int)Math.Pow(width,2);
		}
		else if (destY == startY)
		{
			height = minY;
			width = Math.Abs(startX - destX);
			ans = (int)Math.Pow(height,2) + (int)Math.Pow(width,2);
		}
		else
		{
			height = minX;
			width = Math.Abs(startY - destY);
			int ans1 = (int)Math.Pow(height, 2) + (int)Math.Pow(width, 2);
			height = minY;
			width = Math.Abs(startX - destX);
			int ans2 = (int)Math.Pow(height, 2) + (int)Math.Pow(width, 2);
			ans = Math.Min(ans1, ans2);
		}

		ansList.Add(ans);
	}
	answer = ansList.ToArray();
	return answer;
}

결과 : 실패

3. 세 번째 접근

  • 나는 두 번의 풀이를 할 때 정사각형의 판만 주어진다고 생각하고 풀이를 하고 있었다. 그래서 x나 y가 같은 경우에 한쪽 방향만 제외하고 비교해야 하는데 양쪽 방향을 모두 제외했다.
  • 그래서 모든 방향의 가능성을 거리를 모두 구하고 최소값을 찾는 방법을 선택했다.
  1. 상하좌우벽에 맞았을 때 거리를 구한다.
  2. 예외처리를 하고 최소값을 구한다.
public int[] solution(int m, int n, int startX, int startY, int[,] balls)
{
	int[] answer = new int[] { };

	int startXToLeft = startX;
	int startXToRight = m  - startX;
	int startYToDown = startY;
	int startYToUp = n  - startY;

	List<int> ansList= new List<int>();
	for (int i = 0; i < balls.GetLength(0); i++)
	{
		int destX = balls[i, 0];
		int destY = balls[i, 1];

		int destXToLeft = destX;
		int destXToRight = m - destX;
		int destYToDown = destY;
		int destYToUp = n - destY;
		
        // 모든 방향의 거리
		int left = (int)Math.Pow((startXToLeft + destXToLeft), 2) + (int)Math.Pow(startY - destY, 2);
		int right = (int)Math.Pow((startXToRight + destXToRight), 2) + (int)Math.Pow(startY - destY, 2);
		int down = (int)Math.Pow((startYToDown + destYToDown), 2) + (int)Math.Pow(startX - destX, 2);
		int up = (int)Math.Pow((startYToUp + destYToUp), 2) + (int)Math.Pow(startX - destX, 2);

		int ans;
        
		// 예외처리 
		if (destX == startX)
		{
			if (destY > startY)
				ans = Math.Min(Math.Min(left, right), down);
			else
				ans = Math.Min(Math.Min(left, right), up);
		}
		else if (destY == startY)
		{
			if (destX > startX)
				ans = Math.Min(Math.Min(up, down), left);
			else
				ans = Math.Min(Math.Min(up, down), right);
		}
		else
		{
			ans = Math.Min(Math.Min(left, right), Math.Min( up, down));
		}
		ansList.Add(ans);
	}
	answer = ansList.ToArray();
	return answer;
}
profile
정리노트

0개의 댓글