12月30日 463,464

今天太冷了,回到寝室以后有点累还睡了一会,状态有点差,就两道题

那么进度是-55,过年前应该还是能比较轻松完成的

463. Island Perimeter

小岛的边长

是一个简单题。

还是熟悉的设定,有一个嵌套矩阵,其中0代表是大海,1代表是岛。这个海上有且仅有一个小岛,岛上不存在有湖,也就说不会出现1把0围住的情况,而且所有的1都是相连的,这个说法好像有点奇怪……

我们的认为就是数这个岛的边长

看下图:
示例
黄色的就是代表边长。

确实是简单题,很明显的就知道肯定是一个 O ( n 2 ) O(n^2) O(n2)的解法,就是怎么去优雅的数这个边长

一旦出现相邻的情况的时候,就会少掉两条边,那么我们可以从这点出发。

代码如下:

class Solution(object):
    def islandPerimeter(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        rows, cols = len(grid), len(grid[0])
        res = 0
        for row in range(rows):
            for col in range(cols): 
                if grid[row][col]==1:
                    res+=4
                    if row < rows-1 and grid[row+1][col]==1:
                        res-=2
                    if col < cols-1 and grid[row][col+1]==1:
                        res-=2
        return res

464. Can I Win

有一次游戏叫做’100游戏‘,两个人互相轮流从1到10中选出一个数来,然后求和,如果谁能将和突破100的话,谁就赢了。

现在对这个游戏做一个小小的改动,现在我们认为牌堆的卡是有限的,也就是说1到10都只有1张。

我们给定maxChoosableInteger表示给定的牌的最大值,以及desiredTotal作为游戏目标,超过这个值的那个玩家取得胜利。

那么,在给定的maxChoosableInteger以及desiredTotal下,第一个玩家有没有必胜的方法。

Example

Input:
maxChoosableInteger = 10
desiredTotal = 11

Output:
false

Explanation:
No matter which integer the first player choose, the first player will lose.
The first player can choose an integer from 1 up to 10.
If the first player choose 1, the second player can only choose integers from 2 up to 10.
The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal.
Same with other integers chosen by the first player, the second player will always win.

对于这种轮流出牌的游戏,很显然我们可以用动态规划的想法去做。

假设游戏现在进行到第n轮,牌堆里还有m张牌,现在出牌的这个玩家怎么才能胜利呢。

因为事先假设好了,双方都是会玩的,所以对于无数种方法,只要有一种出牌的顺序能够取得胜利就成立。

现轮到玩家A,玩家A现在有m种选择,我们记为每一种选择的结果为 f ( C a r d − i ) f(Card_{-i}) f(Cardi),其中代表 C a r d − i Card_{-i} Cardi代表牌堆中除了第i张牌。那么这时候

f ( C a r d m ) = a n y ( n o t   f ( C a r d − 1 ) , n o t   f ( C a r d − 2 ) , … , n o t   f ( C a r d − m ) ) f(Card_m) = any(not \text{ } f(Card_{-1}), not\text{ }f(Card_{-2}),\dots, not\text{ }f(Card_{-m})) f(Cardm)=any(not f(Card1),not f(Card2),,not f(Cardm))

因为在A出牌以后游戏轮到了玩家B,而玩家B的胜利就意味着玩家A的失败,玩家A若要胜利就需要找出一条能让玩家B失败的路径。

而胜利的直接条件可以看到就是当牌堆上的最大值大于现在游戏目标,这时候直接出牌堆中的最大值即可获得胜利。

那么代码可以写出来:

class Solution(object):
    def canIWin(self, maxChoosableInteger, desiredTotal):
        """
        :type maxChoosableInteger: int
        :type desiredTotal: int
        :rtype: bool
        """
        return self.helper(range(1, maxChoosableInteger+1), desiredTotal)
    def helper(self, nums, desiredTotal):
        if nums[-1]>=desiredTotal:
            return True
        for i in range(len(nums)):
            if not self.helper(nums[:i]+nums[i+1:], desiredTotal-nums[i]):
                
                return True
        
        return False

但事实上这个进行到第5个实例就失败了,因为复杂度太高了,可以看到整体的复杂度是 O ( N ! ) O(N!) O(N!),第一次出牌有N种可能,第二次出牌有N-1种可能。

减少复杂度的关键就是把中间的结果保存下来,可以看到上面的过程是进行了很多的重复计算。

我们把每次的计算结果保存下来,这时候的复杂度是多少?

这时候我们的复杂度就和牌堆上有多少种牌的组合有关,而和其中的顺序无关了。

那么整体就是 C N 1 + C N 2 + C N 3 + ⋯ + C N N − 1 + C N N = 2 N − 1 C^1_N+C^2_N+C^3_N+\dots+C^{N-1}_N+C^N_N=2^N-1 CN1+CN2+CN3++CNN1+CNN=2N1 O ( 2 N ) O(2^N) O(2N)复杂度的算法,已经比上面的 少很多了。

代码如下:

class Solution(object):
    def canIWin(self, maxChoosableInteger, desiredTotal):
        """
        :type maxChoosableInteger: int
        :type desiredTotal: int
        :rtype: bool
        """
        if (maxChoosableInteger+1)*maxChoosableInteger/2<desiredTotal:
            return False
        self.memo = {}
        return self.helper(range(1, maxChoosableInteger+1), desiredTotal)
    
    def helper(self, nums, desiredTotal):
        
        key = str(nums)
        
        if key in self.memo:
            return self.memo[key]
        
        if nums[-1]>=desiredTotal:
            self.memo[key] = True
            return True
        
    
        for i in range(len(nums)):
            
            if not self.helper(nums[:i]+nums[i+1:], desiredTotal-nums[i]):
                
                self.memo[key] = True
                return True
        self.memo[key] = False
        return False
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值