PYTHON基础


# 前缀和
nums = [1,2,3,4,5]
n = len(nums)
pri = [0] * (n + 1)
for i in range(n):
  pri[i + 1] = pri[i] + nums[i]
# [i,j] 区间的和 pri[j + 1] - pri[i]
for i in range(n):
  for j in range(i, n):
    print(pri[j + 1] - pri[i])

# 二维前缀和
nums = [[1,2,3],[2,3,4],[4,5,6]]
n = len(nums)
m = len(nums[0])
pri = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(n):
  for j in range(m):
    pri[i + 1][j + 1] = pri[i + 1][j] + pri[i][j + 1] - pri[i][j] + nums[i][j]
# [x1][y1] 到 [x2][y2] 的子区间和是 只查询 不用遍历
# pri[x2 + 1][y2 + 1] + pri[x1][y1] - pri[x2 + 1][y1] - pri[x1][y2 + 1]

# 暴力  计算每个 长 a 宽 b 的子区间和
nums = [[1,2,3],[2,3,4],[4,4,5]]
a = 2
b = 2
n = len(nums)
m = len(nums[0])
for i in range(n - a + 1):
  for j in range(m - b + 1):
    su = 0
    for x in range(i,i + a):
      for y in range(j,j + b):
        su += nums[x][y]

# 暴力 计算固定长度的和
nums = [1,23,34]
k = 2
n = len(nums)
for i in range(n - k + 1):
  su = 0
  for x in range(i, i + k):
    su += nums[x]

# 差分 一堆数一次性加上 k
nums = [1,2,4,3]
op = [(1,2),(0,2)]
k = 2
n = len(nums)
d = [0] * (n + 1)  # 差分数组
for x,y in op:
  d[x] += k
  d[y + 1] -= k
# pri = [0] * (n + 1)  使用前缀数组
# for i in range(n):
#   pri[i + 1] = pri[i] + d[i]
# for i in range(1,n + 1):
#   nums[i - 1] += pri[i]
su = 0
for i in range(n):
  su += d[i]
  nums[i] += d[i]

# 二维差分
nums = [[1,2,3],[4,2,3],[3,4,4]]
n = len(nums)
m = len(nums[0])
d = [[0] * (m + 1) for _ in range(n + 1)]
op = [(0,0,1,2),(0,1,2,2)]
for x1,y1,x2,y2 in op:
  d[x1][y1] += k
  d[x2 + 1][y2 + 1] += k
  d[x2 + 1][y1] -= k
  d[x1][y2 + 1] -= k
pri = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(n):
  for j in range(m):
    pri[i + 1][j + 1] = pri[i + 1][j] + pri[i][j + 1] - pri[i][j] + d[i][j]
# 二维子区间和 pri[x2 + 1][y2 + 1] + pri[x1][y1] - pri[x2 + 1][y1] - pri[x1][y2 + 1]
# 二维前缀和 pri[i + 1][j + 1] = pri[i + 1][j] + pri[i][j + 1] - pri[i][j] + nums[i][j]
for i in range(n):
  for j in range(m):
    nums[i][j] += pri[i + 1][j + 1]

# 二分
import bisect
nums = [1,2,3,45,5]
nums.sort()  # 必须是升序数组 有序
k = 2
a = bisect.bisect_left(nums,k)  # 在 nums 中第一个大于等于 k 的位置
b = bisect.bisect_right(nums,k)  # 在 nums 中第一个大于 k 的位置
# 可以用来找在一个数组中小于或大于一个数的数量
bisect.insort_left(nums,a)  # 把 a 插到在 nums 数组中第一个大于等于它的位置
bisect.insort_right(nums,b)  # 把 b 插到在 nums 数组中第一个大于它扥位置

left = 0
right = n - 1
while left <= right:  # 第一个大于等于 k 的位置
  mid = (left + right) // 2
  if nums[mid] >= k:  # 在解决实际问题中 这经常写一个函数来判断满不满足条件 然后寻找答案
    right = mid - 1
  else:
    left = mid + 1
print(left)

# 栈 单调栈 找第一个大于它的数或小于它的数
# 可以找以一个数为最大值的区间
def right_bigger(nums):  # 找第一个大于它的数 找大的 维护递减栈
  stack = []
  n = len(nums)
  ans = [n] * n  # 存的是索引
  for i in range(n):
    while len(stack) != 0 and nums[stack[-1]] < nums[i]:
      ans[stack[-1]] = i
      stack.pop()
    stack.append(i)  # 操作的都是索引
  return ans

def left_bigger(nums):
  stack = []
  n = len(nums)
  ans = [-1] * n
  for i in range(n - 1, -1, -1):
    while len(stack) != 0 and nums[stack[-1]] < nums[i]:
      ans[stack[-1]] = i
      stack.pop()
    stack.append(i)
  return ans

# 队列
# BFS 求最短路 迷宫
from collections import deque
import sys
arr = [
    [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1],
    [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1],
    [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0]
]
# 1 墙 0 路
q = deque([(0,0,0)])  # x y steps
qu = set([(0,0)])  # x y
d = [(0,1),(0,-1),(1,0),(-1,0)]
n = len(arr)
m = len(arr[0])
while q:
  x, y, steps = q.popleft()
  if x == n - 1 and y == m - 1:
    print(steps)
    sys.exit()
  for dx, dy in d:
    nx = dx + x
    ny = dy + y
    if 0 <= nx < n and 0 <= ny < m and arr[nx][ny] == 0 and (nx,ny) not in qu:
      q.append((nx,ny,steps + 1))
      qu.add((nx,ny))
print(-1)  # 没找到
# 可以以当前 q 的长度 作为一轮 for 循环 将当前 q 全部遍历一遍 为了解决一轮一秒类似于
# 可以把所有起点都加入 q

# 双向邻接表用列表 其他的可以用字典
# 这个可以用堆完美代替

# 单调队列 找固定区间的最值
def queue_bigger(nums,k):  # 找一个区间的最大值 维持递减队列
  q = deque()
  ans = []  # 放值
  n = len(nums)
  for i in range(n):
    while len(q) != 0 and nums[q[-1]] <= nums[i]:
      q.pop()  # 弹出队尾元素
    q.append(i)
    if q[0] <= i - k:  # 维护长度 k
      q.popleft()  # 弹出队首元素
    if i >= k - 1:
      ans.append(nums[q[0]])  # 放值
  return ans

# 只有单调栈求左边最值时需要倒序

def queue_samller(nums,k):
  q = deque()
  n = len(nums)
  ans = []  # 放值
  for i in range(n):
    while len(q) != 0 and nums[q[-1]] >= nums[i]:
      q.pop()  # 弹出队尾元素索引
    q.append(i)
    if q[0] <= i - k:
      q.popleft()  # 弹出队首元素索引
    if i >= k - 1:
      ans.append(nums[q[0]])
  return ans

# 哈希表
from collections import Counter
a = Counter(nums)  # 自动统计列表 nums 每个元素的数量
# 是字典

a = set(nums)  # 只保留 nums 中不重复的元素 去重
# 是集合
a.add(1)  # 加元素
a.remove(1)  # 减元素

from collections import defaultdict
a = defaultdict(dict)  # 默认是字典的字典

# DFS
# 模版题 全排列
# but 内置函数
from itertools import permutations
nums = [1,2,3,4]
for perm in permutations(nums):
  print(perm)

n = len(nums)
path = [0] * n
vis = [False] * n
def dfs(depth):
  if depth == n:
    print(path)
    return
  for i in range(n):
    if not vis[i]:
      vis[i] = True
      path[depth] = nums[i]
      dfs(depth + 1)
      vis[i] = False
dfs(0)  # 不要忘了调用好吧

# 求组合
from itertools import combinations
for i in range(1, n + 1):  # 组合的个数
  for com in combinations(nums,i):
    print(com)

path = []
def dfs(depth):
  if depth == n:
    print(path)
    return
  # 不选
  dfs(depth + 1)
  # 选
  path.append(nums[depth])
  dfs(depth + 1)
  path.pop()
dfs(0)

# DFS 就是一个一直回溯的思想 不符合了就返回 返回 剪枝 返回 一直返回
# 标记 去标记

# 连通块
arr = [
    [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1],
    [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1],
    [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0]
]

def dfs(x,y):
  if 0 <= x < n and 0 <= y < m and arr[x][y] == 1:
    arr[x][y] = 0  # 淹没
    return 1 + dfs(x+1,y) + dfs(x-1,y) + dfs(x,y+1) + dfs(x,y-1)
  return 0  # 非陆地返回 0

ans = []
n = len(arr)
m = len(arr[0])
for i in range(n):
  for j in range(m):
    if arr[i][j] == 1:
      ans.append(dfs(i,j))

# 最长公共子序列
s = 'fjsuf'
t = 'sdfjs'
n = len(s)
m = len(t)
dp = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(n):
  for j in range(m):
    if s[i] == t[j]:
      dp[i + 1][j + 1] = dp[i][j] + 1
    else:
      dp[i + 1][j + 1] = max(dp[i + 1][j],dp[i][j + 1])
print(dp[n][m])
# 输出
ans = []
i = n
j = m
while i > 0 and j > 0:
  if s[i - 1] == t[j - 1]:
    ans.append(s[i - 1])
    i -= 1
    j -= 1
  elif dp[i][j] == dp[i][j - 1]:
    j -= 1
  else:
    i -= 1
ans = ''.join(ans[::-1])  # 反转
print(ans)


# 背包问题
# 0 1 背包
total = 1000
w = [23,43,434]
v = [2,3,4]
n = len(w)
dp = [[0] * (total + 1) for _ in range(n + 1)]
for i in range(1,n + 1):
  for j in range(1,total + 1):
    if j >= w[i - 1]:
      dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - w[i - 1]] + v[i - 1])
    else:
      dp[i][j] = dp[i - 1][j]
print(dp[n][total])
# 输出
ans = []
cap = total
for i in range(n,0,-1):
  if cap >= w[i - 1] and dp[i][cap] == dp[i - 1][cap - w[i - 1]] + v[i - 1]:
    ans.append(i - 1)
    cap -= w[i - 1]
ans = ans[::-1]  # 恢复原来顺序
# 如果 w 补零 在 w v 时就不用减 1 了

# 0 1 完全背包
total = 1000
w = [1,23,4,5]
v = [2,3,4,5]
n = len(w)
dp = [[0] * (total + 1) for _ in range(n + 1)]
for i in range(1,n + 1):
  for j in range(1,total + 1):
    if j >= w[i - 1]:
      dp[i][j] = max(dp[i - 1][j],dp[i][j - w[i - 1]] + v[i - 1])
    else:
      dp[i][j] = dp[i - 1][j]
print(dp[n][total])
# 输出
ans  = []
cap = total
for i in range(n,0,-1):
  while cap >= w[i - 1] and dp[i][cap] == dp[i][cap - w[i - 1]] + v[i - 1]:
    ans.append(i - 1)  # 完全背包对一个物品可以一直选 直到不能选
    cap -= w[i - 1]
ans = ans[::-1]  # 恢复原来顺序

# 不同组合的个数
# 达到容量时的个数 就是选该值 或者不选
total = 1000
w = [1,3,4,5,3]
n = len(w)
dp = [[0] * (total + 1) for _ in range(n + 1)]
# 用前 i 个物品凑出容量为 j 的组合数
dp[0][0] = 1  # 容量为 0 时有一种情况 就是空
# dp[i][0] = 1 就是 0 不能忽略容量为 0 的情况 j 要从 0 开始 上面的都可以因为是背包容量 0 时什么都装不了 可以从 1 开始 
for i in range(1,n + 1):
  for j in range(total + 1):  # 从 0 开始
    if j >= w[i - 1]:
      dp[i][j] = dp[i - 1][j] + dp[i - 1][j - w[i - 1]]
    else:
      dp[i][j] = dp[i - 1][j]
print(dp[n][total])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值