알고리즘 문제 풀이

[LeetCode] 877. Stone Game

_OB1N 2021. 8. 6. 15:12

문제 출처: https://leetcode.com/problems/stone-game/

 

문제

알렉스와 리는 돌 더미로 게임을 한다. 한 줄에 짝수개의 돌 더미가 배열되어 있고, 각 더미 piles[i]는 양의 정수 개의 돌이 있다.

 

게임의 목적은 마지막에 가장 많은 돌을 갖는 것이다. 돌의 전체 개수는 홀수개라서 동점은 있을 수 없다.

 

알렉스와 리는 자기 차례에만 플레이를 하며, 알렉스의 차례로 게임이 시작된다. 각 차례에 플레이어는 줄의 시작 또는 끝에서 전체 돌 더미를 가져온다. 더 이상 남은 더미가 없을 때까지 지속되며, 가장 많은 돌을 가진 사람이 승자가 된다.

 

알렉스와 리는 최적의 플레이를 수행한다고 가정하고, 알렉스가 게임에서 이길수 있다면 True를 반환하라.

 

예제

Input: piles = [5,3,4,5]
Output: true

Explanation: 
Alex starts first, and can only take the first 5 or the last 5.
Say he takes the first 5, so that the row becomes [3, 4, 5].
If Lee takes 3, then the board is [4, 5], and Alex takes 5 to win with 10 points.
If Lee takes the last 5, then the board is [3, 4], and Alex takes 4 to win with 9 points.
This demonstrated that taking the first 5 was a winning move for Alex, so we return true.

 

풀이

게임 플레이 규칙에 따라, 한 플레이어는 자신의 차례에 두가지 선택 사항이 존재한다. 만약 dp[i][j]가 piles의 i ~ j 에서 얻을 수 있는 최고의 점수를 저장하고 있는 배열이라 가정한다면, 다음과 같이 선택에 따라 얻을 수 있는 점수를 예측할 수 있다.

  • 플레이어가 첫 돌 더미를 선택한다면,
    • takeStart = sum - dp[i+1][j]
    • sum = i ~ j 범위의 돌 더미(piles)의 합
  • 플레이어가 마지막 돌 더미를 선택한다면,
    • takeEnd = sum - dp[i][j-1]
    • sum = i ~ j 범위의 돌 더미(piles)의 합
  • 최종적으로 두 선택 중에서 더 큰 점수를 얻을 수 있는 선택을 하면 된다.

 

이러한 관계를 코드로 작성하면 다음과 같다.

const N = piles.length;
const dp = new Array(N).fill(0).map(val => new Array(N).fill(0));

var play = function(piles, i, j) {
  if (i == j) {
  	return piles[i];
  }

  if (dp[i][j]) { return dp[i][j]; }

  let sum = 0;
  for (let s = i; s <= j; s++) { sum += piles[s]; }

  const takeLeft = sum - (dp[i+1][j] ? dp[i+1][j] : play(piles, i+1, j));
  const takeRight = sum - (dp[i][j-1] ? dp[i][j-1] : play(piles, i, j-1));

  dp[i][j] = Math.max(takeLeft, takeRight);

  return dp[i][j];
}

이러한 play 함수를 piles의 전체 범위에 대해서 수행하게 되면, dp[0][N-1]에는 게임 시작 플레이어가 얻을 수 있는 최고의 점수가 기록된다.

 

이 플레이어가 이겼는지 확인하기 위해서 돌의 총 개수 중 절반 이상을 해당 플레이어가 얻게 되는지 확인하면 된다.

const totalStone = piles.reduce((a, b) => a + b);

let reuslt = dp[0][N-1] > totalSocre/2;

최종 코드는 아래와 같다.

var stoneGame = function(piles) {
    const N = piles.length;
    const dp = new Array(N).fill(0).map(val => new Array(N).fill(0));
    
    var play = function(piles, i, j) {
        if (i == j) {
            return piles[i];
        }
        
        if (dp[i][j]) { return dp[i][j]; }
        
        let sum = 0;
        for (let s = i; s <= j; s++) { sum += piles[s]; }
        
        const takeLeft = sum - (dp[i+1][j] ? dp[i+1][j] : play(piles, i+1, j));
        const takeRight = sum - (dp[i][j-1] ? dp[i][j-1] : play(piles, i, j-1));
        
        dp[i][j] = Math.max(takeLeft, takeRight);
        
        return dp[i][j];
    }
    
    const totalStone = piles.reduce((a, b) => a + b);
    
    play(piles, 0, N-1);
    
    return dp[0][N-1] > (totalStone/2);
};

 

Github: 877.js

 

GitHub - opwe37/Algorithm-Study

Contribute to opwe37/Algorithm-Study development by creating an account on GitHub.

github.com