暴力递归(二)(python实现)

1、数字转化字符串

  问:规定1和A对应、2和B对应、3和C对应。那么一个数字字符串比如“111",就可以转化为"AAA"、"KA"和"AK"。给定一个只有数字字符组成的字符串str,返回有多少种转化结果。

  解:假设i之前的位置,如何转化已经做过决定了,接下来:首先,i自己作为单独的部分,后续有多少种方法;再考虑(i和i+1)作为单独的部分,后续有多少种方法。如果可以抵达末尾,方法数+1,对于3-9,直接+1跳下一个,对于0,直接返回0可能,对于1,看i+1和i+2,对于2,看是不是20-26,是看i+1和i+2,不是看i+1。代码如下:

class Solution:
    def numConvertStr(self, str):
        if str == None or int(str) == 0:
            return

        res = self.process(str, 0)
        return res

    def process(self, str, i):
        if i == len(str):
            return 1
        if str[i] == '0':
            return 0

        if str[i] == '1':
            res = self.process(str, i + 1)  # i作为单独的部分,后续有多少种方法
            if i + 1 < len(str):
                res+=self.process(str,i+2)  #(i和i+1)作为单独的部分,后续有多少种方法
            return res
        elif str[i] == '2':
            res = self.process(str, i + 1)  # i作为单独的部分,后续有多少种方法
            # (i和i+1)作为单独的部分并且没有超过26,后续有多少种方法
            if i + 1 < len(str) and str[i+1]>='0' and str[i+1]<='6':
                res += self.process(str, i + 2)
            return res
        else:
            return self.process(str,i+1)

 

2、求最大价值

  给定两个长度都为N的数组weights和values,weights[i]和values[i]分别代表i号物品的重量和价值。给定一个正数bag,表示一个载重bag的袋子,你装的物品不能超过这个重量。返回能装下最多的价值。

class Solution:
    def maxValue(self, weight, value, bag):
        res = self.process(weight, value, 0, 0, bag)
        return res

    def process(self, weight, value, i, alreadyWeight, bag):
        # i...的货物自由选择,形成的最大价值返回
        # 重要永远不要超过bag
        # 之前做的决定,所达到的重量,alreadyweight
        if alreadyWeight > bag:
            return 0
        if i > len(weight):
            return 0
        return max(
            self.process(weight,value,i+1,alreadyWeight,bag),
            value[i] + self.process(weight,value,i+1,alreadyWeight+weight[i],bag)
       )

 

3、抽牌博弈问题

  给定一个整型数组arr,代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪明。请返回最后获胜者的分数。

【举例】

  arr=[1,2,100,4]。
  开始时,玩家A只能拿走1或4。如果开始时玩家A拿走1,则排列变为[2,100,4],接下来玩家B可以拿走2或4,然后继续轮到玩家A...
  如果开始时玩家A拿走4,则排列变为[1,2,100],接下来玩家B可以拿走1或100,然后继续轮到玩家A..
  玩家A作为绝顶聪明的人不会先拿4,因为拿4之后,玩家B将拿走100。所以玩家A会先拿1,让排列变为[2,100,4],接下来玩家B不管怎么选,100都会被玩家A拿走。玩家A会获胜,分数为101。所以返回101。
  arr=[1,100,2]。
  开始时,玩家A不管拿1还是2,玩家B作为绝顶聪明的人,都会把100拿走。玩家B会获胜,分数为100。所以返回100。

思想:

用暴力递归的方法:
f(i,j): 表示如果arr[i…j]这个排列上的纸牌被绝顶聪明的人先拿,最终能够获得什么分数。
s(i,j): 表示如果arr[i…j] 这个排列上的纸牌被绝顶聪明的人后拿,最终能获得什么分数。
在先拿的f(i,j)中:
1.如果i == j(只有一张纸牌),会被先拿纸牌的人拿走,所以返回arr[i]或arr[j];
2.如果i != j,先拿纸牌的人有两种选择,要么拿走arr[i],要么拿走arr[j];
1. 如果拿走arr[i],剩下arr[i+1,j]。对于arr[i+1,j]的纸牌,当前玩家成了后拿的人,因此他后续能获得的分数为s(i+1,j)
2. 如果拿走arr[j],那么剩下arr[i,j-1],当前玩家后续能获得的分数为s(i,j-1).
3. 作为绝顶聪明的人,必然会在两种决策中选择最优的。所以返回max{arr[i]+s[i+1,j],arr[j]+s[i][j-1]}
在后拿的s(i,j)中:
1.如果i == j,后拿纸牌的人什么也拿不到,返回0
2.如果i!=j,玩家的对手会先拿纸牌。
1. 对手要么先拿走a[i],那么排列剩下arr[i+1,j],
2. 要么先拿走arr[j],剩下arr[i,j-1]
3. 对手也是绝顶聪明的人,所以也会把最差的情况留给玩家因此返回min{f(i+1,j),f(i,j-1)}

class Solution:
    def win(self, arr):
        if not arr:
            return None
        res = max(self.first(arr, 0, len(arr) - 1), self.second(arr, 0, len(arr) - 1))

    def first(self, arr, left, right):
        if left == right:
            return arr[left]
        return max(
            arr[left] + self.second(arr, left + 1, right),
            arr[right] + self.second(arr, left, right - 1)
        )

    def second(self, arr, left, right):
        if left == right:
            return 0
        return min(
            self.first(arr, left + 1, right),
            self.first(arr, left, right - 1)
        )

 

4、纸牌博弈先手最优问题

  问题:

一个纸牌明牌显示,牌上的数值就是分数:(就是一个数组,每个数均为非负整数),设置一个数为M,每个人都是绝顶聪明,都是按最优策略顺序拿牌,

一次可拿的牌数是大于等于1,小于等于M,问先手拿牌的人最多可以得几分。

答:这题使用贪心法绝对是不行的。

比如 1,1,1,100,设置M=2

那么第一个人绝对不能第一次拿两张牌,否则第二个人就可以拿到100.

这涉及到两个人博弈问题,但是博弈策略是一样的,所以可以使用同一个函数表示博弈策略

class Solution:
    def maxValue(self, arr, m):
        if arr == None:
            return 0

        return self.first(arr, 0, m)

    def first(self, arr, index, m):
        if index >= len(arr):
            return 0
        lens = len(arr) - index  # 计算出数组剩余长度是否大于m
        value = 0
        if lens <= m:   # 如果数组长度小于m,那就全部拿过来
            value = sum(arr[index:])
            return value
        else:
            allValue = sum(arr)
            for i in range(1, m):
                if allValue - self.first(arr, index + i, m) > value:    # 用总值减去第二个人的最大值,对m种情况都测试比较
                    value = allValue - self.first(arr, index + i, m)

            return value

 

5、n皇后问题(32以内)

  N皇后问题是指在N*N的棋盘上要摆N个皇后,要求任何两个皇后不同行、不同列,也不在同一条斜线上。

给定一个整数n,返回n皇后的摆法有多少种。

n=1,返回1。
n=2或3,2皇后和3皇后问题无论怎么摆都不行,返回0。
n=8,返回92。

class Solution:
    def nQueue(self, n):
        if n < 1:
            return 0
        record = [0 for _ in range(n)]
        res = self.process(0, record, n)
        return res

    def process(self, i, record, n):
        if i == n:
            return 1
        res = 0
        for j in range(0, n):
            # 当前i行的皇后,放在列,会不会和之前(e..i - 1)的皇后,共行共列或者共斜线,
            # 如果是,认为无效
            # 如果不是,认为有效
            if self.isValid(record, i, j):
                record[i] = j
                res += self.process(i + 1, record, n)
        return res

    def isValid(self,record, i,j):
        for k in range(0,i):
            if j == record[k] or abs(record[k]-j) == abs(i-k):
                return False
        return True

  使用位运算加速(理解)

class Solution:
    def nQueue(self, n):
        if n < 1 or n > 32:
            return 0
        upperLim = 0
        if n == 32:
            upperLim = -1
        else:
            upperLim = (1 << n) - 1
        res = self.process2(upperLim, 0, 0, 0)
        return res

    # colLim 列的限制,1的位置不能放皇后,0的位置可以
    # leftDiaLim左斜线的限制,1的位置不能放皇后,0的位置可以
    # rightDiaLim右斜线的限制,1的位置不能放皇后,0的位置可以
    def process2(self, limit, colLim, leftDiaLim, rightDiaLim):
        if colLim == limit:
            return 1
        pos = limit & (~(colLim | leftDiaLim | rightDiaLim))  #pos代表能在哪些位置上放皇后
        res = 0
        while pos != 0:
            mostRightOne = pos & (~pos + 1) # 提取pos最右侧的1
            pos = pos - mostRightOne
            res += self.process2(limit, colLim | mostRightOne, (leftDiaLim | mostRightOne) << 1,
                                 (rightDiaLim | mostRightOne) >> 1)

        return res

 

posted @ 2020-05-13 14:35  r1-12king  阅读(262)  评论(0编辑  收藏  举报