1. 程式人生 > >度小滿2018.9.26筆試 小遊戲

度小滿2018.9.26筆試 小遊戲

題目描述

在這裡插入圖片描述 這是球盒問題中的,n個球有區別,m個盒子有區別,且盒子可以為空(但球都放進盒子裡)的這種情況,這種情況的方案數為mnm^n,而在本題中,方案數則為aba^b

根據題意表述以及輸入輸出,可以發現輸出其實輸出是輸的那一方(出題人把輸出弄錯了)。為了方便理解,這裡讓輸出為贏的一方。所以: 樣例輸入: 3 2 2 10 3 1 4 1 4 10 樣例輸出: A B A&B

思路

在每個回合,A和B兩個人都只能增加a,或者增加b,所以每種狀態只有兩個分支(增加底數或增加指數)。可以想象,如果考慮所有分支,那麼就能畫出一個二叉樹。 本文認為,如果只考慮當前狀態的兩個分支,然後再判斷下一步走哪個分支,只能算是貪心演算法。而題目說了雙方都是採取的最優策略

,則應該考慮雙方都知道所有分支的結果,即知道所有分支的二叉樹

那麼怎麼畫這顆二叉樹呢,考慮增加底數為左孩子,增加指數為右孩子。如果左右孩子都小於n,那麼畫出左右孩子;如果只有左孩子小於n,那麼只畫左孩子;右孩子同理;如果左右孩子都大於等於n,那麼該節點為葉子節點。 在這裡插入圖片描述 以輸入為2 2 10為例,所有的葉子節點的高度%2==1。且這種情況,A贏。 在這裡插入圖片描述 以輸入為3 1 4為例,所有的葉子節點的高度%2==0。且這種情況,B贏。 總結:

  • 所有的葉子節點再往下走都會大於等於n,所以它們是一個葉子節點。
  • 如果,所有的葉子節點的高度%2==1,那麼肯定是A贏。
  • 如果某個葉子節點的高度%2==1,那麼從根節點到該葉子節點的這種流程,是A贏。
  • 如果,所有的葉子節點的高度%2==0,那麼肯定是B贏。
  • 如果某個葉子節點的高度%2==0,那麼從根節點到該葉子節點的這種流程,是B贏。

但如果二叉樹中,兩種葉子節點都有,這種情況要複雜點: 我們為葉子節點賦值,如果高度%2==1,那麼賦值1。如果高度%2==0,那麼賦值0。即節點為1代表A贏,節點為0代表B贏。葉子節點的值,能夠自底向上地傳遞,傳遞規則如下: 被傳遞肯定只有非葉子節點。分析只有一個孩子的非葉子節點,那麼孩子的值就是該節點的值。 分析有兩個孩子的非葉子節點,這裡分兩種情況。

1.非葉子節點的高度%2==0,那麼該節點的分支是由A決定的,那麼該節點的兩個孩子只要有一個1,那麼該節點就為1。即left or right。因為這個節點的分支是A決定的,所以只要有一個分支為1那麼A自然肯定就會選擇為1的這條分支。

2.非葉子節點的高度%2==1,那麼該節點的分支是由B決定的,那麼該節點的兩個孩子只要有一個0,那麼該節點就為0。即left and right。因為這個節點的分支是B決定的,所以只要有一個分支為0那麼B自然肯定就會選擇為0的這條分支。 在這裡插入圖片描述 如上圖所示,假設畫出來的二叉樹是這樣。那麼初始時,有三個葉子節點。 b)中,由於是隻有一個孩子的非葉子節點,所以直接傳遞值。 c)中,由於該節點是由B決定分支,所以取1 and 0即0。 d)中,由於該節點是由A決定分支,所以取1 or 0即1。 所以,最終是A贏。

還有一種特殊情況是a=1 因為底數為1,那麼二叉樹的右孩子能一直產生(1的任何次冪都為1,如果此時n還大於1),但左孩子到達某個高度後就會沒有左孩子了(因為此時b很大,a一旦從1變成2,就會不小於n)。 這種情況下多了平局的情況,而且注意此時雙方會在輸和平局兩種分支中,選擇平局。 在這裡插入圖片描述 以起始為1,4為例,現假設畫出來的二叉樹為這樣,紅色位元組點之後可能還會有分支,但是在本圖中不畫出來,而是看作這些紅色位元組點已經被傳遞到了值。 思路是從上到下分析紅色位元組點。 1.首先是2,4,因為其父節點是A作決定,所以如果2,4被傳遞到的值為1,那麼A贏,但如果值為0,那麼走右分支,結果待定,還得看下一個紅色位元組點。 2.然後如果到了2,5,因為其父節點是B作決定,所以如果2,5被傳遞到的值為0,那麼B贏,但如果值為1,那麼走右分支,結果待定,還得看下一個紅色位元組點。 3.之後就是這樣,交替分析下一個紅色位元組點。 4.最後如果走到了只有右分支的節點,那麼此時平局。

程式碼

T = eval(input())

def solve(a,b,n,step):
    #此函式處理a=1的情況
    left = (a+1) ** b
    right = a ** (b+1)
    if (left >= n) & (right >= n):#遞迴終點,也是二叉樹中的葉子節點
        if step%2 == 1:
            return 1
        elif step%2 == 0:
            return 0   

    #遞迴過程
    if left >= n:#只有右孩子可以走
        return solve(a,b+1,n,step+1)
    elif right >= n:#只有左孩子可以走
        return solve(a+1,b,n,step+1)
    else:#左右孩子都可以走
        leftResult = solve(a+1,b,n,step+1)
        rightResult = solve(a,b+1,n,step+1)
        if step%2 == 1:#B決定
            return leftResult and rightResult
        elif step%2 == 0:#A決定
            return leftResult or rightResult
def solve_one(b,n):
    #此函式處理a>1的情況
    step = 1
    left = 2 ** b
    if left >= n:
        return None
    else:
        while(True):
            if 2 ** b >= n:
                break
            result = solve(2,b,n,step)
            if (step%2 == 1) and (result == 1):
                return 1
            if (step%2 == 0) and (result == 0):
                return 0            
            b += 1
            step += 1 
    return None

for i in range(T):
    a,b,n = map(int,input().split())
    result = 0
    if a != 1:
        result = solve(a,b,n,0)
    else:
        result = solve_one(b,n)
    if result is 1:
        print('A')
    elif result is 0:
        print('B')
    else:
        print('A&B')

輸入: 4 2 2 10 3 1 4 1 4 10 1 4 17 在這裡插入圖片描述

使用短路與、短路或

在這裡插入圖片描述 這種情況中,其實根本不用傳遞所有值,因為根節點是由A節點決定的,既然有一個孩子為1,那麼另一個孩子的值也就無所謂了。所以程式碼可以這樣優化。

def solve(a,b,n,step):
    #此函式處理a=1的情況
    left = (a+1) ** b
    right = a ** (b+1)
    if (left >= n) & (right >= n):#遞迴終點,也是二叉樹中的葉子節點
        if step%2 == 1:
            return 1
        elif step%2 == 0:
            return 0   

    #遞迴過程
    if left >= n:#只有右孩子可以走
        return solve(a,b+1,n,step+1)
    elif right >= n:#只有左孩子可以走
        return solve(a+1,b,n,step+1)
    else:#左右孩子都可以走
        if step%2 == 1:#B決定
            return solve(a+1,b,n,step+1) and solve(a,b+1,n,step+1)
        elif step%2 == 0:#A決定
            return solve(a,b+1,n,step+1) or solve(a+1,b,n,step+1)

A決定的節點能短路1,B決定的節點能短路0。所以: 在A決定的節點中,要把左右孩子中,更可能為1的孩子放在前面。 在B決定的節點中,要把左右孩子中,更可能為0的孩子放在前面。 雖然說了如上兩個結論,但左右孩子的值到底更可能為0還是1,這是一件不確定的事情(根據a,b,n的取值來決定,而且分析起來挺複雜)。如上程式碼,我是這樣放的:A決定的就先放右孩子,B決定的就先放左孩子。