백준

BOJ 2600

E재HO 2023. 8. 7. 12:02

구슬게임 

 

두 사람 A와 B가 번갈아 가면서 두 개의 구슬 통에서 몇 개씩의 구슬을 꺼내는 게임을 한다.

한번에 한 사람이 한 통에서 꺼낼 수 있는 구슬의 개수는 세 가지 뿐이다. 그리고 구슬을 꺼낼 경우 두 개의 구슬 통 중에서 하나를 마음대로 선택해서 그 안에서만 꺼낼 수 있다. 즉 두 개의 통 모두에서 동시에 몇 개씩 꺼낼 수는 없다.

게임은 항상 A가 먼저하고 그 다음 B, 그 다음 A 순으로 번갈아가면서 진행된다. 그리고 자신의 차례가 되었을 때에 정해진 규칙대로 구슬을 꺼낼 수 없는 사람이 게임에서 지게 되고, 상대방은 승리하게 된다.

예를 들어 한번에 꺼낼 수 있는 구슬의 개수를 1개, 3개, 또는 4개라고 하자. 만일 두 개의 구슬 통에 각각 4개, 1개의 구슬이 있다고 하면 처음 선택을 하게 되는 A가 이긴다. 그러나 만일 두 통속의 구슬이 각각 5개, 5개라면 B가 이긴다.

즉 한번에 꺼낼 수 있는 구슬 개수인 b1, b2, b3가 주어지고, 두 구슬 통 속에 들어있는 구슬의 수인 k1, k2이 정해지면, 이러한 b1, b2, b3와 k1, k2에 따라서 승패는 결정된다. 문제는 주어진 b1, b2, b3와 k1, k2에 대하여 A, B중 누가 승자인지를 결정하는 것이다.

처음 두 통 속에 들어 있는 구슬의 수 k1, k2와 한 번에 꺼낼 수 있는 구슬의 수 b1, b2, b3에 대한 제한조건은 다음과 같다.

  • 1 ≤ b1 < b2 < b3 ≤ 30 
  • 1 ≤ k1, k2 ≤500

입력

첫 줄에는 한번에 꺼낼 수 있는 구슬의 개수를 나타내는 세 개의 정수 b1, b2, b3 가 나타난다. 그 다음 5개의 각 줄에는 두 통속에 처음 담겨있는 구슬의 개수 k1, k2가 각각 표시되어 있다.

출력

각 5개의 k1, k2 경우에 대하여 그에 대응되는 승자(A 또는 B)를 각각 한 줄에 하나씩 차례대로 다섯 개를 출력해야 한다.

dp문제

A가 결국 한방만 이기게 하면 됌. 이기는 단 하나의 경우가 있다면 그 경우로 가서 이기면 되니까.

A가 처음부터 구슬 꺼내는 단계에 따라 탑다운 방식으로 진행

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;


public class Main {
    static int[][][] dp = new int[2][501][501];
    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int [] value = new int[3];

        for(int i =0; i<3; i++) value[i]= Integer.parseInt(st.nextToken());
        for(int a=0; a<2; a++)
            for(int i=0;i<=500; i++)
                for(int j =0; j<=500; j++)
                    dp[a][i][j]=-1;

        for(int i =0; i<5; i++){
            st = new StringTokenizer(br.readLine());
            int f = Integer.parseInt(st.nextToken());
            int s = Integer.parseInt(st.nextToken());

            if(game(value,f,s,false)==1)System.out.println("A");
            else System.out.println("B");
        }
    }
    private static int game(int[] value, int f, int s, boolean flag){
        int AorB=0;
        if(flag ==true)AorB=1;

        if(dp[AorB][f][s]!=-1)return dp[AorB][f][s];

        for(int i=0; i<3; i++)
            if(f-value[i]>=0 && game(value,f-value[i],s,!flag)==0)return dp[AorB][f][s]=1;

        for(int i=0; i<3; i++)
            if(s-value[i]>=0 && game(value,f,s-value[i],!flag)==0)return dp[AorB][f][s]=1;

        //맨 밑에 depth 까지 내려와봄
        //여기 도달 했다는 것은 A나B가 더이상은 구슬을 선택할 수 없다는 뜻임==졌다는 뜻임
        //그래서 A또는 B의 구슬 상태 f s에는 졌다는 도장을 쾅 찍는 것임
        //그리고 여기서 0이 리턴되기때문에 이전단계로 올라가서 상대는 이겼다는 걸 리턴해줌
        //그러면 또 그위로 올라가서 다른 선택지로 가봄 또 누군가는 이기고 지고의 도장을 받고 상위로 졌다 이겼다를 알려줌
        //그럼 첫번쨰로 돌아와서 앞선 판에서 졌다는 게 하나라도 올라온다면
        //만약 game (value, 4, 1, false) 이상태에서 함수로 들어왔는데
        //return false 가 다음 단계에서 올라온다면 현 단계는 true 가 되고 그건 이겼다는 것을 의미함.
        //다시 올라가서 모든 첫 시작으로 파생된 모든 선택에 도장을 찍음
        return dp[AorB][f][s]=0;
    }

}