Algorithm/LeetCode
[LeetCode] Divisor Game (Dynamic Programming)
JunGi Jeong
2021. 3. 9. 13:35
약 한시간 가량 풀어봤는데 풀리지 않아서 Discuss답변을 봤습니다.
leetcode.com/problems/divisor-game/discuss/979796/Java-all-approach-Easy-Recursive-Memoization-DP
문제 이해를 위한 설명
- 이 문제는 쉽지만 이해하기 어렵습니다.
- 문제에서 Alice는 1부터 N까지 숫자를 선택할 수 있고, 자신의 승리를 최적화하기 위해 선택합니다. Bob도 마찬가지로 최선을 다합니다.
- 숫자가 2이면 Alice의 순서로 시작하여 1을 선택하고 이깁니다.
- 3일 경우 Alice가 1을 선택하고 밥이 1을 선택하여 앨리스가 집니다.
- 숫자 4일 경우
4 = Alice -> (4-1) - gives 3 to Bob
3 = Bob -> (3-1) - gives 2 to Alice
2 - Alice -> (2-1) - gives 1 to Bob
- 이제 Bob은 아무것도 선택할 수없고
- 이렇게 모든 숫자를 시도하면 N이 홀수라는 것을 알면 앨리스가 지고 , N이 짝수라면 앨리스가 이기는 패턴을 얻게됩니다.
- 이 문제에 대한 간단한 해결책은 N % 2 == 0
O (1)
class Solution {
public boolean divisorGame(int N) {
return N % 2 == 0;
}
}
- 이 문제는 재귀함수로도 해결할 수 있습니다.
- N== 1 return false, N == 2 return true
- Alice와 Bob은 1부터 N까지의 숫자 중 하나를 선택할 수 있습니다.
- N % i == 0인 수 중에 최적의 수를 찾습니다.
- 그런 다음 다른 사람에게 남은 번호로 선택권이 제공됩니다.
O(N) - Top Down Recursive Solution
class Solution {
public boolean divisorGame(int N) {
if (N==1) return false;
if (N==2) return true;
for (int i=1;i<=N;i++){
if (N%i == 0)
return !(divisorGame(N-i));
}
return false;
}
}
- 재귀를 사용하면 하위 문제를 반복해서 계산하는 경향이 있습니다.
- 따라서 캐시를 도입하면 이미 계산 된 값을 캐시하는 데 도움이 됩니다.
- 또한 1에서 sqrt (N)까지 반복 할 수 있습니다. 예를 들어 N = 16 인 경우 선택할 수있는 유일한 최적 요인은 다음과 같습니다.
1x16 = 16
2x8 = 16
4x4 = 16
O(N) - Recursive with Memoization
class Solution {
public boolean divisorGame(int N) {
boolean[] cache = new boolean[N+1];
if (N== 1){
cache[1] = false;
return false;
}
if (N== 2){
cache[2] = true;
return true;
}
for (int i=1;i*i<=N;i++){
if (N % i == 0){
cache[i] = !(divisorGame(N-i));
return cache[i];
}
}
return false;
}
}
Bottom up Approach
- 상향식 dp 접근 방식을 사용하여이 문제를 해결할 수도 있습니다.
- DP 테이블에 boolean 배열을 N + 1까지 저장
- 1에서 N을 선택하도록 반복합니다.
- j가 각 플레이어가 선택한 숫자를 반복하도록 합니다.
- 승리 한 사람이 있으면 이전 계산 된 값과 함께 현재 값 dp [i]를 저장합니다! dp [i-j]
- 마지막으로 dp [N] 반환
class Solution {
public boolean divisorGame(int N) {
boolean[] dp = new boolean[N+1];
for(int i=1;i<=N;i++)
{
if(i==1) dp[i]=false;
else if(i==2) dp[i]=true;
else{
for(int j=1;j*j<=i;j++)
{
if(i%j==0)
{
dp[i] = !dp[i-j];
break;
}
}
}
}
return dp[N];
}
}