【问题标题】:So I tried to make a recursive function but it results in infinite loop所以我尝试制作一个递归函数,但它导致无限循环
【发布时间】:2021-09-17 12:54:34
【问题描述】:

我正在尝试编码挑战Link

对于没有帐户的人,这是复制的: 彼得和亨利正在玩一个由字符“0”和“1”组成的长度为 n 的字符串 s 的游戏。两位玩家轮流轮流,但彼得先轮流。

在每一回合中,玩家可以对字符串执行以下操作之一:

选择任何 i,其中 s[i]= '0' 并将 s[i] 更改为 '1' 并支付 1 卢比。另一个操作是将整个字符串反转并支付 0 卢比。仅当字符串当前不是回文并且最后执行的操作不是反向时才允许此操作。如果彼得倒弦,那么亨利下一回合就不能倒弦,反之亦然。

反转字符串意味着将其字母从最后一个到第一个重新排序。比如“01001”反转后会变成“10010”。

当字符串的每个字符都变为“1”时,游戏结束。到此为止花费最少卢比的玩家获胜,如果双方花费相同的卢比,则为平局。如果两名球员都发挥最佳,确定谁是赢家或是否平局。

输入格式

第一行包含一个整数 t 。然后是 t 个测试用例。

每个测试用例的第一行包含一个整数 n。

每个测试用例的第二行包含长度为 n 的字符串 s,由字符 '0' 和 '1' 组成。保证字符串 s 至少包含一个 '0'。

输出格式

对于每个测试用例,在新行中打印一个单词:

“PETER”,如果彼得会赢得比赛,“HENRY”,如果亨利会赢得比赛,“DRAW”,如果比赛以平局结束。

样本输入 0

2 3 110 2 00

样本输出 0

彼得 亨利

解释0

在示例的第一个测试用例中,在第 1 步中,Peter 将使用第 2 次操作来反转字符串,因为执行第 1 次操作无论如何都会导致他的损失。这也迫使亨利使用第一个操作。在第二步中,亨利必须执行第一次操作,因为第二次操作不能连续执行两次。字符串的所有字符都是'1',游戏结束。彼得花费 0 卢比,而亨利花费 1 卢比。因此,彼得赢了。

这是代码,我是递归函数的新手,此代码导致最大递归限制。任何帮助将不胜感激。

def Winner(c,counter):
    if sum(c) == b:
        if len(set(counter.values)) == 1:
            print("DRAW")
            return
        else:
            d=index(min(counter.values()))
            print(counter.keys(d))
            return
    if c == c[::-1]:
        for i in c:
            if i==0:
                c[i] =1
            break
        counter["PETER"] +=1
        c=c[::-1]
        print(c)
        Winner(c,counter)
    else:
        c= c[::-1]
        for i in c:
            if i==0:
                c[i]=1
            break
        counter["HENRY"] +=1
        Winner(c,counter)
a=int(input())
p,h=0,0
counter={"PETER":0,"HENRY":0}
global b
for i in range(a):
    b=int(input())
    c=list(map(int,input().strip().split()))
    Winner(c,counter)

【问题讨论】:

    标签: python-3.x dictionary recursion list-comprehension user-defined-functions


    【解决方案1】:

    我花了几天时间试图理解你的代码和它的逻辑,但我被它无法运行的事实所阻碍,而且它的布局逻辑似乎有缺陷。结果,我自己分析了游戏问题并构建了自己的解决方案。为此,为了清楚地说明我认为的游戏规则,我已尽可能简洁明了地重申规则:

    游戏

    Peter 和 Henry 正在玩一个由字符 '0' 和 '1' 组成的长度为 n 的字符串 s 的游戏。两位玩家轮流轮流,但彼得总是第一轮。

    戏剧

    在每一回合中,玩家可以对字符串执行以下操作之一:

    1. 选择任意 i,其中 s[i]= '0' 并将 s[i] 更改为 '1' 并支付 1 卢比。
    2. 反转整个字符串并支付 0 卢比。如果出现以下情况,则不允许使用此选项:
      • 上一个玩家反串了
      • 字符串是回文

    结束游戏

    当字符串的每个字符都变成'1'时游戏结束。
    花费最少的玩家获胜。如果双方花费相同,则为平局。

    问题

    如果两名球员都发挥最佳,确定谁会赢,或者比赛是否平局。

    分析

    假设以上内容准确描述了游戏玩法:
    任何解决方案都需要解决的第一个问题是确定“最佳玩法”的含义。通过初步观察,我们可以推断出任何玩家的最佳玩法都必须符合与:

    1. 如果可能,选择字符串反转 - 这会迫使对手在点球时选择数字
    2. 如果反转不是一个选项,请选择一个要反转的数字,这会导致回文。这要求对手也转换一个数字,因此惩罚是相等的
    3. 如果无法创建回文,请选择索引并更改值,惩罚为 1

    但经过进一步评估,这不一定是正确的优先级,例如:

    开始:10100
    玩家 0 播放 00101 得分(0,0)
    玩家 1 播放 10101 分数(0,1)
    玩家 0 播放 11101 得分(1,1)
    玩家 1 播放 10111 得分(1,1)
    玩家 0 播放 11111 得分(2,1)
    玩家 1 获胜

    或者
    玩家 0 播放 10101 得分(1,0)
    玩家 1 播放 11101 得分(1,1)
    玩家 0 播放 10111 得分(1,1)
    玩家 1 打出 11111 得分(1,1)
    玩家 0 获胜

    如上所示,玩家 0 可以通过在第一步创建回文而不是反转字符串来改善结果。因此,最佳的定义取决于游戏的最终结果,而不是简单地从任何给定游戏阶段的可用选项中选择最佳游戏。

    实现此逻辑的方法要求首先确定所有游戏状态,然后在每个回合为玩家采取最佳的最终游戏解决方案路径。

    然后,第一步需要我们创建所有游戏状态的连接图 (GameTree)。为了跟踪游戏每一步的游戏状态,我创建了一个 GameState 类。此类的一个实例将用于定义 GameTree 的每个节点。每个 GameState 实例都会定义:

    • 输入得分 [玩家 1,玩家 2]
    • 在此状态下移动的玩家
    • 输入字符串
    • 之前的动作是反向标记 True/False
    • 之前的游戏状态
    • 子游戏状态
    • 每个孩子的终点得分

    因此,使用上面的示例,生成的简化树将符合以下条件,其中每个节点由 (Player, String_state, score, children) 描述:

    GS0=[0: '10100', (0,0), [GS1, ..]]  
        GS1 = [1: '00101', (0,0), [GS3, ..]]  
             GS3 = [0, '10101', (0,1), [GS4, ..]]  
                   GS4 = [1: '11101', (1,1). [GS5]]  
                         GS5 = [0: '10111', (1,1), [GS6, ..]]  
                               GS6 = [1: '11111', (2,1), []]  
                   
             
        GS2 = [1: '10101', (1,0), [GS7, ..]]  
              GS7 = [0: '11101', (1,1), [GS8, ..]] 
                    GS8 = [1: '10111', (1,1), [GS9]]  
                         GS9 = [0: '11111', (1,2), []]
        
    

    一旦定义了 GameTree 图,就可以使用深度优先搜索 (DFS) 算法沿着树的每个分支向下移动,直到找到叶节点(即没有子节点的节点),然后返回到前一个节点的叶子节点得分。在每个有多个选项可用的节点上,tghe 函数将为玩家选择最佳解决方案并将该解决方案传递给前一个节点,这将继续返回,直到搜索完所有路径并且第一个玩家的最佳最终游戏移动可以是决定。以下代码实现了这种方法。

    # Utility functions to assist in implementing optimal play
    def is_palindrome(z):
        # Returns True if s is a palindrome
        return z == z[::-1]
    
    def find_palindrome(s):
        """ Returns a list of indexes that if switched 
        will result in a palindrome position"""
        rslt = []
        k = ''
        for i, d in enumerate(s):
            k += d
            if d == '0':
                j = k[:i] + '1' + s[i+1:]
                if is_palindrome(j):
                    rslt.append(i)
        return rslt    
    
        
    def get_bestScore(l, plr):
        """ Return best score for existing move options"""
        best = None
        scr = None
        for sc in l:
            rslt = sc[(plr+1)%2] - sc[plr]
            if best == None:
               best = rslt
               scr = sc 
            else:
                if rslt > best:
                    best = rslt
                    scr = sc
        return scr
    
    def find_BestMove(node):
        # Recursive function to search for leaf nodes and bactrack to define best play option
        ptr = 0
        if node.children:
            while len(node._end_options) < len(node.children):
                node._end_options.append(find_BestMove(node.children[ptr]))
                ptr += 1
            return get_bestScore(node._end_options, node.player)                        
        else:
            return node.score   
    
    #Class to maintain game tree node states
    class GameState:
    
        def __init__(self, parent, str_in, rvflg, score):
            self._prior = parent
            self._str = str_in
            self._player = 0
            self._score = score
            self._rvflg = rvflg
            self._children = []
            self._end_options = []
            
            if self._prior:
                self._player = (self._prior.player + 1)%2
            self._children.extend(self.get_move_options())
            
        def __gt__(self, other):
            return self.score[self.player] < other.score[other.player]
        
        def __repr__(self):
            return f"GS{self._id:02} - [{self.player}: {self.state_string} => {self.score}"
        
        def get_move_options(self):
            ''' return a list of next game states for each possible move 
            from current postion.  The list is sorted so that the best moves are
            at the begining of the list'''   
            mv_opts = []
            if not self.rvflg and not is_palindrome(self._str):
                mv_opts.append(GameState(self, self._str[::-1], True, self.score))
            sc = self._score.copy()
            sc[self._player] += 1
            popts = find_palindrome(self._str)
            for i, d in enumerate(self._str):
                if d == '0' and i not in popts:
                    popts.append(i)
            for m in popts:
                nw_str = self._str[:m]+'1'+self._str[m+1:]
                mv_opts.append(GameState(self, nw_str,  False, sc))  
            return mv_opts
        
        
        @property
        def state_string(self):
            return self._str
        
        @property
        def rvflg(self):
            return self._rvflg
        
        @property
        def score(self):
            return self._score
        
        @property
        def player(self):
            return self._player
        
        @property
        def  children(self):
            return self._children
    
    def playGame(instr):
        # Function to play the Game by building the Gamestate tree and playing optimum moves
        players = ["Peter", 'Henry']          
        cgs = GameState(None,instr, False, [0,0])
        rslt = find_BestMove(cgs)
        out_str = ''
        if rslt[0] == rslt[1]:
            out_str = 'Draw'
        elif rslt[0] < rslt[1]:
            out_str = players[0]
        else:
            out_str = players[1]
        return out_str  
    

    作为使用上述代码的示例:

    test_cases = [('00', 'Henry'), ('100', "Draw"), ('10100', 'Peter'), ('0010', 'Peter')]
    for tc in test_cases:
        print(f"For Input String '{tc[0]}', Expected {tc[1]} and got: {playGame(tc[0])}")
    

    产生以下输出:

    For Input String '00', Expected Henry and got: Henry
    For Input String '100', Expected Draw and got: Draw
    For Input String '10100', Expected Peter and got: Peter
    For Input String '0010', Expected Peter and got: Peter
    ​
    

    【讨论】:

    • 谢谢,我花了好几天才理解这段代码,因为我是 DSA 的新手,但现在我已经理解了。非常感谢
    • 欢迎您,建议的代码效率不高,对于超过 10 个字符的输入运行时间太长,因此可以进行改进。
    猜你喜欢
    • 2015-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-15
    相关资源
    最近更新 更多