209. 长度最小的子数组
太久没做题初始思路只能想到暴力破解,看了一眼提示可能会用到前缀和,能够想到只要建立一个新数组,bi = a0 + a1 + ... + ai 即数组a的前缀,这样子序列i到j就可以表示为bj - bi-1 ,由于数组元素是大于1的,所以b数组必然是递增的,那么在计算子序列的时候,当符合条件的子序列出现后记录下来,接着就可以先向右移动后下标,移动后不满足条件后,再移动左下标,直到满足条件,如此往复。但如何实现没有特别好的思路,复杂度好像也还是很大,自己的思路到此为止。
看完代码随想录,发现自己的思路没有大的问题,但是在细节方面自己没有能够想通,总感觉差了一点细节,在这里做一些补充。
滑动窗口:类似双指针
先对终止位置进行右移,此时的操作是将子序列sum += 终止下标的数;
判断此时sum是否大于等于target:
如果符合,需要判断符合的子序列长度是否小于上一次的sum(这里我自己有个纠结的思考,其实画图会清楚很多,而且这一步思考的意义可能不大,记录一下:会以为终止下标右移到符合条件之后,一定会导致【之后的子序列长度】都大于【上一次最后符合条件的长度】,因此导致【之后能得出的结果】都是大于【上一次最后符合条件的长度】的,这个想法走进了误区,是错误的,因为虽然第一次右移到符合条件的位置时,会大于等于【上一次最后符合条件的长度】,但在之后会进行起始下标的右移,使得子序列的长度不断减小,直到小于【上一次最后符合条件的长度】,也就是从上一次符合到本次符合的过程中,起始下标可能右移了多次,并且【起始下标右移次数】>【终止下标右移次数】,使得符合的子序列长度变小,这个过程自己记录的尽可能详细了,但文字表述难免混乱模糊,只作为自己理解,看到这的朋友不理解请见代码随想录讲解视频:拿下滑动窗口! | LeetCode 209 长度最小的子数组_哔哩哔哩_bilibili
如果不符合,则继续将终止下标右移。
思路过程文字表述如上
另外解答了我初始思路中时间复杂度的疑问,感觉进行了两层循环,时间复杂度好像还是很大的问题:
这里强调一下,不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数
了解上述思路后,开始用python实现代码(再次感叹上面C++精髓代码的精简):
补充:在Python中,float('inf') 表示正无穷大。这是一个特殊的浮点数值,用于表示超出浮点数表示范围的数值。当你尝试进行一个结果超出浮点数最大表示范围的计算时,Python会返回 float('inf')。
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
length = len(nums)
i, result, sum = 0, float('inf'), 0 #i为起始下标, result为【当前“得到过的”最小子序列“长度”】, sum为【当前子序列】之“和”
for j in range(length):
sum += nums[j]
while sum >= target: # 当子序列之和大于target的时候进入循环,开始尝试缩小滑动窗口以获得最小值
result = min(result, j-i+1)
sum -= nums[i]
i += 1
# 退出循环后,有符合条件的返回result,否则返回0
if result < float('inf'):
return result
else:
return 0
规范代码比自己的可读性稍强点,放作对比:
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
l = len(nums)
left = 0
right = 0
min_len = float('inf')
cur_sum = 0 #当前的累加值
while right < l:
cur_sum += nums[right]
while cur_sum >= s: # 当前累加值大于目标值
min_len = min(min_len, right - left + 1)
cur_sum -= nums[left]
left += 1
right += 1
return min_len if min_len != float('inf') else 0
59.螺旋矩阵II
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
示例 1:
输入:n = 3 输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:
输入:n = 1 输出:[[1]]
提示:
1 <= n <= 20
四个运动状态,且变化都是固定的,暂时没想到什么好办法,只能枚举硬解。
整理如下:
运动方向: 向右 下个状态变化: 向下/停止
运动方向: 向下 下个状态变化: 向左
运动方向: 向左 下个状态变化: 向上/停止
运动方向: 向上 下个状态变化: 向下
Right:向右时,首先为自己赋值,然后判断右边是否为数组内且为空,是的话向右执行right,如果右边不为空或者超出数组范围,则向下执行down,如果下边也不为空或者超出数组范围,则返回。
Down:向下时,首先为自己赋值,然后判断下边是否为数组内且为空,是的话向下执行down,如果下边不为空或者超出数组范围,则向左执行left,如果左边也不为空或者超出数组范围,则返回。
Left:向左时,首先为自己赋值,然后判断左边是否为数组内且为空,是的话向左执行left,如果左边不为空或者超出数组范围,则向上执行up,如果上边也不为空或者超出数组范围,则返回。
Up:向上时,首先为自己赋值,然后判断上边是否为数组内且为空,是的话向上执行up,如果上边不为空或者超出数组范围,则向右执行down,如果右边也不为空或者超出数组范围,则返回。
(由于要考虑n=1的情况,所以把每个方向的两种情况都考虑了,实际情况可能不需要,所以存在优化空间,自己画图模拟了一下,发现好像只有向右和向左的情况下会停下来成为终点。而且完成最外围一圈之后,剩下的部分起始是新的完整的一圈。原本考虑想用递归,发现不是很好用甚至有点忘了咋怎么用。。决定换个写法,但过程不变,写完发现实在过于暴力,可维护性非常地差,并且debug了很多次,虽然过了,但非常不推荐)
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
direction = 'r'
count = 1
x, y = 0, 0
nums = [[0 for _ in range(n)] for _ in range(n)]
while count <= n ** 2:
nums[x][y] = count
if direction == 'r': # 向右
if (y + 1) < n and nums[x][y + 1] == 0: # 未超出范围且数组为空
y += 1
direction = 'r'
elif (y + 1 < n and nums[x][y + 1] != 0) or y + 1 >= n: # 未超出但数组不为空(已填)或者超出范围,这类情况均转向
x += 1
direction = 'd'
else: # 向右和向左存在终止的情况,可能返回
return nums
elif direction == 'd': # 向下的情况,除了不会终止过程,其他过程与上述类似。
if x + 1 < n and nums[x + 1][y] == 0:
x += 1
direction = 'd'
elif (x + 1 < n and nums[x + 1][y] != 0) or x + 1 >= n:
y -= 1
direction = 'l'
elif direction == 'l':
if y - 1 >= 0 and nums[x][y - 1] == 0:
y -= 1
direction = 'l'
elif (y - 1 >= 0 and nums[x][y - 1] != 0) or y - 1 < 0:
x -= 1
direction = 'u'
else:
return nums
else:
if x - 1 >= 0 and nums[x - 1][y] == 0:
x -= 1
direction = 'u'
elif (x - 1 >= 0 and nums[x - 1][y] != 0) or x - 1 < 0:
y += 1
direction = 'r'
count += 1
return nums
虽然结果过了,但这过程确实很痛苦,要足够细心和耐心,尤其代码可维护性相当差,非常不推荐。下面附上学习卡哥的题解,感觉简单很多,就是需要认真看看视频理解一下。
核心是:循环不变量原则、左闭右开
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
nums = [[0] * n for _ in range(n)]
startx, starty = 0, 0 # 起始点
loop, mid = n // 2, n // 2 # 迭代次数、n为奇数时,矩阵的中心点
count = 1 # 计数
for offset in range(1, loop + 1) : # 每循环一层偏移量加1,偏移量从1开始
for i in range(starty, n - offset) : # 从左至右,左闭右开
nums[startx][i] = count
count += 1
for i in range(startx, n - offset) : # 从上至下
nums[i][n - offset] = count
count += 1
for i in range(n - offset, starty, -1) : # 从右至左
nums[n - offset][i] = count
count += 1
for i in range(n - offset, startx, -1) : # 从下至上
nums[i][starty] = count
count += 1
startx += 1 # 更新起始点
starty += 1
if n % 2 != 0 : # n为奇数时,填充中心点
nums[mid][mid] = count
return nums
这里不多说什么了。。只要耐心总有人能A,但是理解了核心方法才有可能在短时间内做出来。
区间和
题目描述
给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。
输入描述
第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间,直至文件结束。
输出描述
输出每个指定区间内元素的总和。
输入示例
5
1
2
3
4
5
0 1
1 3
1
2
3
4
5
6
7
8
输出示例
3
9
1
2
数据范围:
0 < n <= 100000
def main():
n = int(input())
nums = [None] * n
sum = [None] *n
for i in range(n):
val = int(input())
nums[i] = val
sum [0] = nums[0]
for i in range(n-1):
sum[i+1] = sum[i] + nums[i+1]
while True:
try:
ab = input().split()
start, end = int(ab[0]), int(ab[1])
print(int(sum[end]) - int(sum[start]) + int(nums[start]))
'''
#这个位置今天debug了很久,一开始的写法是
# print(sum[ab[1]] - sum[ab[0]] + nums[ab[0]])
#后面发现一直不过,原因可能如下:
#在 print(int(sum[ab[1]]) - int(sum[ab[0]]) + int(nums[ab[0]])) 这一行,如果 ab[0] 和 ab[1] 是数组的索引,那么 sum[ab[1]] 可能会超出数组的索引范围,因为 ab[1] 应该是一个数字,而不是直接用作索引。输入的 ab 应该是两个数字,代表查询的起始和结束位置。但是,程序没有对这些输入进行有效的错误处理或验证。
'''
except:
break
if __name__ == "__main__":
main()
今日另外一个纠错:
#ab=int(input().split()) 这个是错误的
这样的代码是不正确的,因为它试图将一个列表转换为整数,这会导致错误。input().split() 会返回一个字符串列表,即使你只输入了一个数字。
如果你想要读取一个数字,并且确保它是一个整数,你应该这样做:
python
ab = int(input())
如果你想要读取两个数字,并将它们存储在两个变量中,你可以这样做:
python
a, b = map(int, input().split())
这里,input().split() 会读取一行输入并将其分割成多个字符串,map(int, ...) 会将这些字符串转换为整数。
今天使用的ACM模式,以前掌握了C++的写法,头回使用python,还是非常难受的,不过有收获,下一次使用应该不会再浪费那么多时间了。
提供规范代码,写的比自己好很多,需要学习
import sys
input = sys.stdin.read
def main():
data = input().split()
index = 0
n = int(data[index])
index += 1
vec = []
for i in range(n):
vec.append(int(data[index + i]))
index += n
p = [0] * n
presum = 0
for i in range(n):
presum += vec[i]
p[i] = presum
results = []
while index < len(data):
a = int(data[index])
b = int(data[index + 1])
index += 2
if a == 0:
sum_value = p[b]
else:
sum_value = p[b] - p[a - 1]
results.append(sum_value)
for result in results:
print(result)
if __name__ == "__main__":
main()
44. 开发商购买土地
【题目描述】
在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。
现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。
然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。
为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。
注意:区块不可再分。
【输入描述】
第一行输入两个正整数,代表 n 和 m。
接下来的 n 行,每行输出 m 个正整数。
输出描述
请输出一个整数,代表两个子区域内土地总价值之间的最小差距。
【输入示例】
3 3 1 2 3 2 1 3 1 2 3
【输出示例】
0
【提示信息】
如果将区域按照如下方式划分:
1 2 | 3 2 1 | 3 1 2 | 3
两个子区域内土地总价值之间的最小差距可以达到 0。
【数据范围】:
- 1 <= n, m <= 100;
- n 和 m 不同时为 1。
初始思路没有什么特别的,实现思路很快完成,但是这个ACM模式真的又是卡了我好久。。希望明天不要又是这个情况了。
思路:先将需要的数组数值填满,由于需要切一刀将总土地分成两份,所以在遍历数组的时候,分为两种情况,我用两个循环实现,复杂度不意外的是O(n2)。
- 从左向右遍历,同时记录遍历到的加数总和total_x,每到达一次右边界的时候计算一次差值,相当于到达右边界的时候进行了横切一刀的操作。表达式为|total_上-total_下| = |total - 2*total_上|
- 从上往下遍历,同理到达下边界的时候计算一次差值,相当于到达下边界的时候进行了竖切一刀。
def main():
data = input().split()
n = int(data[0])
m = int(data[1])
space = [[0 for _ in range(m)] for _ in range(n)]
total = 0
total_x, total_y = 0, 0
result = float('inf')
for i in range(n):
row = input().split()
for j in range(m):
space[i][j] = int(row[j])
total += space[i][j]
for i in range(n):
for j in range(m):
total_x += space[i][j]
if j == m-1:
result = min(result, abs(total-2*total_x))
for j in range(m):
for i in range(n):
total_y += space[i][j]
if i == n-1:
result = min(result, abs(total-2*total_y))
print(result)
if __name__ == "__main__":
main()
和规范代码的实现差不多,思路基本没问题。还是得吐槽,又在acm的输入上浪费了太多的时间。。。
以下是规范代码:
def main():
import sys
input = sys.stdin.read
data = input().split()
idx = 0
n = int(data[idx])
idx += 1
m = int(data[idx])
idx += 1
sum = 0
vec = []
for i in range(n):
row = []
for j in range(m):
num = int(data[idx])
idx += 1
row.append(num)
sum += num
vec.append(row)
result = float('inf')
count = 0
# 行切分
for i in range(n):
for j in range(m):
count += vec[i][j]
# 遍历到行末尾时候开始统计
if j == m - 1:
result = min(result, abs(sum - 2 * count))
count = 0
# 列切分
for j in range(m):
for i in range(n):
count += vec[i][j]
# 遍历到列末尾时候开始统计
if i == n - 1:
result = min(result, abs(sum - 2 * count))
print(result)
if __name__ == "__main__":
main()
标签:59,nums,209,sum,随想录,int,range,result,数组
From: https://blog.csdn.net/weixin_47681529/article/details/142186124