1. 程式人生 > >取球博弈問題 藍橋杯

取球博弈問題 藍橋杯

取球博弈這個問題思路比較明確就是將兩個人所有可能取的情況進行遍歷,最後看兩個人手中球數量奇偶。遍歷的話可採用深度優先搜尋
不過直接深度優先搜尋肯定不行,因為如果有1000個球那麼每次有三種取法,一共差不多就有3的幾百次方。這個資料簡直是天文數字。
因此需要進行記憶化儲存,將原先算過的資料進行儲存。但是要儲存所有資料也是不現實的,因為我們需要儲存的結果是當剩下n個球,自己有m個球,別人有k個球時候的輸贏。那麼需要一個三維陣列a[n][m][k],而n,m,k最大為1000,就需要1000M左右記憶體也不現實。
怎麼辦呢? 還是看題目要求,是要我們判斷最後兩個人球的奇偶,也就是說我們只要管奇偶性就可,也就是說當剩下300個球,自己3個球,別人8個球的情況和當剩下300個球,自己5個球,別人2個球這種情況最後輸贏是一樣的。因為自己球再多也是奇數,別人球再少也是偶數,而可選是球總數一樣所以最後奇偶性也是一樣的,輸贏也一樣。

取球博弈
兩個人玩取球的遊戲。
一共有N個球,每人輪流取球,每次可取集合{n1,n2,n3}中的任何一個數目。
如果無法繼續取球,則遊戲結束。
此時,持有奇數個球的一方獲勝。
如果兩人都是奇數,則為平局。
假設雙方都採用最聰明的取法,
第一個取球的人一定能贏嗎?
試程式設計解決這個問題。
輸入格式:
第一行3個正整數n1 n2 n3,空格分開,表示每次可取的數目 (0<n1,n2,n3<100)
第二行5個正整數x1 x2 ... x5,空格分開,表示5局的初始球數(0<xi<1000) 
輸出格式:
一行5個字元,空格分開。分別表示每局先取球的人能否獲勝。
能獲勝則輸出+,
次之,如有辦法逼平對手,輸出0
, 無論如何都會輸,則輸出- 例如,輸入: 1 2 3 1 2 3 4 5 程式應該輸出: + 0 + 0 - 再例如,輸入: 1 4 5 10 11 12 13 15 程式應該輸出: 0 - 0 + + 再例如,輸入: 2 3 5 7 8 9 10 11 程式應該輸出: + 0 0 0 0 資源約定: 峰值記憶體消耗(含虛擬機器) < 256M CPU消耗 < 3000ms

首先我們把基礎的深度優先搜尋程式碼寫出來,不要管記憶的問題。至少在小資料上是妥妥的過。下面是不加記憶的深度優先搜尋。

public class SelectBall {
    public static int[] a;
    public
static int min; public static int[][] cash = new int[1001][1001]; public static int f(int rest, int havenum, int othernum) { if (rest < min) { if (havenum % 2 != 0 && othernum % 2 == 0) return 2; if (havenum % 2 != 0 && othernum % 2 != 0) return 1; if (havenum % 2 == 0 && othernum % 2 == 0) return 1; return 0; } boolean equalflag = false; for (int sel : a) { if (sel > rest) continue; // f(1,0,1) int result = f(rest - sel, othernum, havenum + sel); if (result == 0) return 2; if (result == 1) equalflag = true; } if (equalflag) return 1; else return 0; } public static void main(String[] args) { int[] b = { 1, 2, 3, 4, 5 }; a = new int[] { 1, 2, 3 }; min = 1; for (int total : b) System.out.println(f(total, 0, 0)); } }

下面是進行記憶化儲存的程式碼:

public class SelectBall {
    public static int[] a;
    public static int min;
    public static int[][][] cash = new int[1001][2][2];

    public static int f(int rest, int havenum, int othernum) {
        if (rest < min) {
            if (havenum % 2 != 0 && othernum % 2 == 0)
                return 2;
            if (havenum % 2 != 0 && othernum % 2 != 0)
                return 1;
            if (havenum % 2 == 0 && othernum % 2 == 0)
                return 1;
            return -1;
        }
        boolean equalflag = false;
        for (int sel : a) {
            if (sel > rest)
                continue;
            int result;
            if (cash[rest - sel][othernum % 2][(havenum + sel) % 2] != 0)
                result = cash[rest - sel][othernum % 2][(havenum + sel) % 2];
            else {
                result = f(rest - sel, othernum, havenum + sel);
                cash[rest - sel][othernum % 2][(havenum + sel) % 2] = result;
            }
            if (result == -1)
                return 2;
            if (result == 1)
                equalflag = true;
        }
        if (equalflag)
            return 1;
        else
            return -1;
    }

    public static void main(String[] args) {
        int[] b = { 1, 2, 3, 4, 5, 900 };
        a = new int[] { 1, 2, 3 };
        min = 1;
        for (int total : b) {
            char ch = 0;
            switch (f(total, 0, 0)) {
            case -1:
                ch = '-';
                break;
            case 1:
                ch = '0';
                break;
            case 2:
                ch = '+';
                break;
            }
            System.out.print(ch);
        }
    }
}