有点忧伤,博弈题没做出来,前面还wa了好几次。
第一题 第 N 个泰波那契数
题目大意
泰波那契序列 Tn 定义如下:
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给你整数 n
,请返回第 n 个泰波那契数 Tn 的值。
解题思路
类似于斐波那契数列,只不过将两个数的和变成了三个数的和。
代码如下
class Solution:
def tribonacci(self, n: int) -> int:
t_list = [0,1,1]
length = len(t_list)
while length <= n + 1:
t_list.append(t_list[length - 1] + t_list[length - 2] + t_list[length - 3])
length += 1
return t_list[n]
第二题 字母板上的路径
题目大意
我们从一块字母板上的位置 (0, 0)
出发,该坐标对应的字符为 board[0][0]
。
在本题里,字母板为board = ["abcde", "fghij", "klmno", "pqrst", "uvwxy", "z"]
.
我们可以按下面的指令规则行动:
如果方格存在,'U'
意味着将我们的位置上移一行;
如果方格存在,'D'
意味着将我们的位置下移一行;
如果方格存在,'L'
意味着将我们的位置左移一列;
如果方格存在,'R'
意味着将我们的位置右移一列;
'!'
会把在我们当前位置 (r, c)
的字符 board[r][c]
添加到答案中。
返回指令序列,用最小的行动次数让答案和目标 target
相同。你可以返回任何达成目标的路径。
解题思路
题目的关键点:如果方格存在,又没注意,wa了一次。
这是一个6*5的不完整矩阵,最后一行只有一个'z'。如果不考虑最后一个'z',那么路径即为x_new - x_old 再 y_new - y_old 地跑。那么再考虑一下'z'这种特殊情况,如果上一个字母为'z',我们要跑去下一个非'z'字母,那一定是先向上跑,因为右边已经过不去了;另一种情况,我们要从其它字母跑去'z',那么我们可以先跑到第一列,然后再向下跑。
代码如下
class Solution:
def alphabetBoardPath(self, target: str) -> str:
x, y = 0, 0
ans = ''
for each in target:
ascii_num = ord(each)
new_x = (ascii_num - 97) // 5
new_y = (ascii_num - 97) % 5
if x == 5:
#如果上一个是'z',就先上下方向跑,然后再左右方向跑
if new_x - x < 0:
for i in range(x - new_x):
ans += 'U'
elif new_x - x > 0:
for i in range(new_x - x):
ans += 'D'
if new_y - y < 0:
for i in range(y - new_y):
ans += 'L'
elif new_y - y > 0:
for i in range(new_y - y):
ans += 'R'
else:
#其它情况(为了照顾目的地为'z'),可以先左右跑,再上下跑
if new_y - y < 0:
for i in range(y - new_y):
ans += 'L'
elif new_y - y > 0:
for i in range(new_y - y):
ans += 'R'
if new_x - x < 0:
for i in range(x - new_x):
ans += 'U'
elif new_x - x > 0:
for i in range(new_x - x):
ans += 'D'
ans += '!'
x = new_x
y = new_y
return ans
第三题 最大的以 1 为边界的正方形
题目大意
给你一个由若干 0
和 1
组成的二维网格 grid
,请你找出边界全部由 1
组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回 0
。
解题思路
没考虑到一些细节性问题,又wa了好几次。。。比如边长为1的情况
边界全为1,如果边长为1,那么边界和为1;如果边长为n(n > 1),那么边界和为 n*4 - 4(每个顶点都算了2次);考虑到这一步,题目就转化成了如何求边界和的值。边界和的值 = 正方形的总值 - 内部正方形的值,然后问题就解决了,具体看代码实现。
代码如下
class Solution:
def largest1BorderedSquare(self, grid: List[List[int]]) -> int:
n = len(grid)
m = len(grid[0])
#计算长方形([0,0],[i,j])的值,0行0列空出来,方便计算
sum_grid = [[0 for j in range(m + 1)] for i in range(n + 1)]
for i in range(n):
for j in range(m):
sum_grid[i + 1][j + 1] = sum_grid[i][j + 1] + sum_grid[i + 1][j] - sum_grid[i][j] + grid[i][j]
for k in range(min(n, m) + 1, 0, -1):
for i in range(n):
for j in range(m):
i2 = i + k - 1
j2 = j + k - 1
if i2 < n and j2 < m:
#总正方形的值
totalsum = sum_grid[i2 + 1][j2 + 1] - sum_grid[i][j2 + 1] - sum_grid[i2 + 1][j] + sum_grid[i][j]
#如果边长大于2,减去内部正方形的值
if k > 2:
in_x1, in_y1 = i + 1, j + 1
in_x2, in_y2 = i2 - 1, j2 - 1
totalsum -= sum_grid[in_x2 + 1][in_y2 + 1] - sum_grid[in_x1][in_y2 + 1] - sum_grid[in_x2 + 1][in_y1] + sum_grid[in_x1][in_y1]
#判断边界和是否为全一,注意边长为一的特例
if k > 1:
if totalsum == k * 4 - 4:
return k*k
else:
if totalsum == 1:
return k
return 0
第四题 石子游戏 II
题目大意
许多堆石子 排成一行,每堆都有正整数颗石子 piles[i],
亚历克斯和李轮流拿石头,亚历克斯先开始。最初,M = 1
。
在每个玩家的回合中,该玩家可以拿走剩下的 前 X
堆的所有石子,其中 1 <= X <= 2M
。然后,令 M = max(M, X)
。
游戏一直持续到所有石子都被拿走。
假设亚历克斯和李都发挥出最佳水平,返回亚历克斯可以得到的最大数量的石头。
解题思路
博弈题,设f[ i ][ j ]为从第 i 块石头拿到第 n 块石头,M为 j 时的最大值。
状态转移:当M为j时,我可以拿 1 ~ 2j 块石头,当我拿取k块石头后,又到了对方的回合,这时候他要从i+k至n块石头中找最优策略,故状态转移方程为f[ i ][ j ] = max(f[ i ][ j ],sum[i 至 n] - f[i + k][max(j , k)]) (1<=k<= 2*j)
最后的结果即为f[1][1](从第一块石头开始拿,且M为1)
代码如下
class Solution:
def stoneGameII(self, piles: List[int]) -> int:
n = len(piles)
sum_piles = [0]
for i in range(n):
sum_piles.append(sum_piles[len(sum_piles) - 1] + piles[i])
f = [[0 for j in range(n + 1)] for i in range(n + 2)]
for i in range(n, 0, -1):
for j in range(1, n + 1):
for k in range(1, min(2 * j + 1, n + 1)):
next_step = min(i + k, n + 1)
f[i][j] = max(f[i][j], sum_piles[n] - sum_piles[i - 1] - f[next_step][max(k, j)])
return f[1][1]