Leetcode 刷题摘录
0. 常用操作模板
(1)I/O模板
Python 基本I/O
import sys
n = int (input ())
for line in sys.stdin:
arr = line.split()
while True :
line = sys.stdin.readline().strip()
values = list (map (int , line.split()))
if line == '' :
break
while True :
try :
nums = list (map (int , input ().split(' ' )))
print (sum (nums))
except :
break
(2)工具函数
常用 python api
from functools import reduce
map (function, list_of_inputs)
reduce(function, iterable[, initializer])
from collections import Counter
cnt_e = Counter(tuple (sorted (e)) for e in edges)
from collections import defaultdict
d = defaultdict(int )
from queue import Queue
q = Queue()
from collections import deque
dq = deque()
from sortedcontainers import SortedList
prefix_sum_list = SortedList([...], key=None )
(3)排序函数
Python 中内置 2 个排序函数
一个是 list 的 sort() 方法,另一个是全局的 sorted() 方法:
list.sort(key=None, reverse=False)
,将list自身进行排序,返回None,默认升序;reverse=True降序、False升序。
sorted(iterable [,cmp=None [,key=None [,reverse=True]]])
,返回排好序的新列表,不改变对象本身,默认升序;reverse=True降序、False升序。对所有可迭代的对象均有效。
(python2限定)cmp指定一个定制的比较函数cmp(x,y)
,这个函数接收两个参数(x, y
, iterable的元素)。如果 x < y 返回 -1, 如果 x == y 返回 0, 如果 x > y 返回 1。更具体些,-1在排序中代表不改变x,y
位置,1代表变成y,x
位置。一个简单实现:return x-y
。
python3不支持cmp参数,而key函数并不直接比较任意两个原始元素,它把原始元素转换成一个新的可比较的关键字对象,代替原始元素去参与比较。如:key=lambda x: x.element
。
多字段排序时,如排序元素为tuple,默认按第一、第二元素依次排序。对于复杂数据结构,可以采用函数functools.cmp_to_key(cmp)
可以将自定义的比较函数cmp转化为关键字函数key,与接受key函数的api一同使用(如 sorted(), min(), max(), heapq.nlargest(), itertools.groupby()
)。
1. 数学 问题
1.1 位运算
从集合论到位运算:通过二进制表示集合,实现状态压缩。 灵茶山艾府: 位运算技巧分类总结
a, b = 6 , -5
a ^ b == c
(a ^ b) ^ z == a ^ (b ^ c)
a ^ b ^ b == a ^ 0 == a
~a == -(a+1 )
bin (a)
r = b & 0xffffffff
int (bin (r), 2 )
a.bit_length()
a.bit_count()
lowbit = a & -a
【1-1】枚举 N 元集合的 k 元子集
Gosper’s Hack是一种生成 n元集合所有 k元子集的算法,它巧妙地利用了位运算
def GospersHack (k:int , n:int ):
cur = (1 << k) - 1
limit = 1 << n
while cur < limit:
lb = cur & -cur
r = cur + lb
cur = (((r ^ cur) >> 2 ) // lb) | r
1.2 常用数学操作
AcWing常用代码模板4——数学知识
【1-2-1】求两个数的 最大公因数 GCD
@cache
def gcd (x:int , y:int ) -> int :
return x if y==0 else gcd(y, x%y)
def gcd_multi (nums ):
res = nums[0 ]
for n in nums:
while n:
res, n = n, res % n
return res
【1-2-2】求两个数的 最小公倍数 LCM
@cache
def lcm (a:int , b:int ) -> int :
x, y = a, b
while b:
t, a = a, b
b = t % b
return x/a*y
【1-3】GrayCode 格雷码 模板
def grayCode (n ):
res = [0 ]
i = 0
while i < n:
next_base = 1 << i
res_right = [x + next_base for x in res]
res.extend(list (reversed (res_right)))
i += 1
for x in res:
print (bin (x))
【1-4】快速幂 模板
def fastExpMod (x:float , n:int ):
res, p = 1.0 , abs (n)
while p:
if (p & 1 ):
res *= x
p >>= 1
x *= x
return res if n >= 0 else 1. / res
def newtomSqrt (self, x ):
r = x + 1
while r*r > x:
r = int ((r+x/r)/2 )
return r
【1-5】质数相关
质数筛选
def countPrimes (n ):
if n < 2 :
return 0
isPrime = [True ] * (n + 1 )
isPrime[0 ] = isPrime[1 ] = False
i = 2
while i * i <= n:
if isPrime[i]:
for j in range (i*i, n+1 , i):
isPrime[j] = False
i += 1
return sum (isPrime)
分解质因数
from math import isqrt
from collections import Counter
def divide (x: int ):
primes = Counter()
for i in range (2 , isqrt(x)+1 ):
while x % i == 0 :
x /= i
primes[i] += 1
if x > 1 :
primes[x] = 1
return primes
【1-6】大数运算 模拟
def addStrings (self, num1: str , num2: str ) -> str :
res = ""
i, j, carry = len (num1) - 1 , len (num2) - 1 , 0
while i >= 0 or j >= 0 :
n1 = int (num1[i]) if i >= 0 else 0
n2 = int (num2[j]) if j >= 0 else 0
tmp = n1 + n2 + carry
carry = tmp // 10
res = str (tmp % 10 ) + res
i, j = i - 1 , j - 1
return "1" + res if carry else res
def multiply (num1, num2 ):
product = [0 ] * (len (num1) + len (num2))
position = len (product) - 1
for n1 in num1[::-1 ]:
tempPos = position
for n2 in num2[::-1 ]:
product[tempPos] += int (n1) * int (n2)
product[tempPos - 1 ] += product[tempPos] // 10
product[tempPos] %= 10
tempPos -= 1
position -= 1
pointer = 0
while pointer < len (product) - 1 and product[
pointer] == 0 :
pointer += 1
return '' .join(map (str , product[pointer:]))
【1-7】组合数取模
讲解:组合数取模
def pow (a:int , b:int , p:int ):
r, x = 1 , a
while b:
if b&1 :
r = r * a % p
a = a * a % p
b >>= 1
return r
def comb (a:int , b:int , p:int ):
b = a-b if a-b < b else b
r = 1
for i in range (1 , b+1 ):
x, y = (a-b+i), i%p
r = r * (x * pow (y, p-2 , p) % p) % p
return r
factorial = [1 ] * (N+1 )
for i in range (2 , N+1 ):
factorial[i] = factorial[i-1 ] * i % p
def comb (a:int , b:int , p:int ):
b = a-b if a-b < b else b
return factorial[a] * pow (factorial[b], p-2 , p) % p * pow (factorial[a-b], p-2 , p) % p
def lucas (n:int , m:int , p:int ):
return comb(n%p, m%p, p) * lucas(n//p, m//p, p) % p
【1-8】异或之和 模板
bits = Counter()
for x in nums:
for i in range (32 ):
bits[i] += 1 if x&1 else 0
x >>= 1
S = 0
for i in range (32 ):
S += ((bits[i] * (N-bits[i])) << i) % MOD
【1-x】其他数学操作
for i in range (n):
for j in range (i, n):
matrix[j][i], matrix[i][j] = matrix[i][j], matrix[j][i]
Cat[n] = C(2 *n, n) / (n + 1 )
2. 线性表
2.1 数组
循环数组 :数组首尾相接,最后一个元素的后继为第一个元素。一般通过 % 求模运算 来模拟 next_idx = (idx + 1) % N
。多数情况也采用将数组复制一遍再连接到末尾来模拟环形数组。
【2-0】 数组 基本算法
前缀和
pre = [0 ]*(N+1 )
for i in range (N):
pre[i+1 ] = pre[i] + nums[i]
pre = [[0 ] * (M+1 ) for _ in range (N+1 )]
for i in range (N+1 ):
for j in range (M+1 ):
pre[i][j] = pre[i-1 ][j] + pre[i][j-1 ] - pre[i-1 ][j-1 ] + nums[i][j]
差分数组
差分可以看成前缀和的逆运算,我们始终保持:原数组a 是 差分数组d 的前缀和数组。
d [ i ] = a [ 0 ] ( i == 0 ) = a [ i ] − a [ i − 1 ] ( i >= 1 ) d [ i ] = a [ 0 ] ( i == 0 ) = a [ i ] − a [ i − 1 ] ( i >= 1 )
有如下性质:
从左到右累加 差分数组 d 中的元素,可以得到 原数组 a 。
如下两个操作是等价的:
区间操作 :将数组a的子区间 [i, j]
的元素都加上 x 。
单点操作 :将数组 d[i]
增加 x,d[j+1]
减少 x(对于j+1==N
则无需减操作)。
利用上面的性质:可以O(1)
复杂度完成 数组 a 上闭区间[l, r]
的加减操作,然后通过 性质1
复原出数组a。
d = [0 ] * N
d[0 ] = a[0 ]
for i in range (1 , N):
d[i] = a[i] - a[i-1 ]
d[l] += c
d[r+1 ] -= c
二维差分 :https://zhuanlan.zhihu.com/p/338258918
2.2 链表
2.3 栈
栈(stack) 是一种简单的线性数据结构,遵循后进先出 的逻辑顺序,符合某些问题的特点,如 函数调用栈。
单调栈 实际上就是栈,只是利用了一些巧妙的逻辑,使得每次新元素入栈后,栈内的元素都保持有序(单调递增或单调递减)。
2.4 堆
【2-1】单调栈 模板
def func (nums ):
st = []
res = [0 ]*len (nums)
for i, x in enumerate (nums):
while st and nums[st[-1 ]] < x:
idx = st.pop()
res[idx] = i-idx
st.append(i)
return res
【2-2】并查集 模板
parent = list (range (N))
def find (x ):
if x != parent[x]:
parent[x] = find(parent[x])
return parent[x]
def union (x, y ):
root1 = find(x)
root2 = find(y)
parent[root2] = root1
【2-3】 模板
【2-4】 模板
【2-5】 模板
3. 动态规划
3.1
最大连续子数组和 :
使字符串变为非递减串的最少修改次数 : 参考
dp[i][j]
: 前 i 个字符串为非递减 且 最后一个字符为 j 的最少修改次数;min(dp[i-1][k])
表示前i-1
个字符形成非递减且最大字符小于等于k
的最少修改次数。
当s[i] == j
时即第i
个字符恰好是j
时。不需要修改s[i]
,dp[i][j] = min(dp[i-1][k])
其中 k<=j
(保证非递减);
当s[i] != j
时即第i
个字符不是j
时,需要将s[i]
改成字符j
再加上 min(dp[i-1][k])
。 dp[i][j] = min(dp[i-1][k]) + 1
其中 k<=j
(保证非递减)
【3-1】数位DP模板
@cache
def f (i:int , pre:int , isLimit:bool , isNum:bool ) -> int :
if i == len (s):
return int (isNum)
res = 0
if not isNum:
res += f(i+1 , 0 , False , False )
up = int (s[i]) if isLimit else 9
for d in range (1 -int (isNum), up+1 ):
cnt = f(i+1 , pre*10 +d, isLimit and d==up, True )
res += cnt
return res
n = len (num2)
num1 = num1.zfill(n)
@cache
def dfs (i: int , s: int , limit_low: bool , limit_high: bool ) -> int :
if s > max_sum:
return 0
if i == n:
return s >= min_sum
lo = int (num1[i]) if limit_low else 0
hi = int (num2[i]) if limit_high else 9
res = 0
for d in range (lo, hi + 1 ):
res += dfs(i + 1 , s + d, limit_low and d == lo, limit_high and d == hi)
return res
【3-2】最长xx子序列 模板
求数组的最长上升子序列 LIS (Longest increasing subsequence)
def lis_1 (nums: List ):
dp = []
for i in range (len (nums)):
idx = bisect_left(dp, nums[i])
if idx == len (dp):
dp.append(nums[i])
else :
dp[idx] = nums[i]
return len (dp)
def lis_2 (nums: List ):
dp = [0 ] * len (nums)
for i in range (len (nums)):
dp[i] = 1
for j in range (i):
if nums[j] < nums[i]:
dp[i] = max (dp[i], dp[j]+1 )
return max (dp)
def longestCommonSubsequence (text1: str , text2: str ) -> int :
m, n = len (text1), len (text2)
dp = [[0 ] * (n+1 ) for _ in range (m+1 )]
for i in range (1 , m+1 ):
for j in range (1 , n+1 ):
if text1[i-1 ] == text2[j-1 ]:
dp[i][j] = dp[i-1 ][j-1 ] + 1
else :
dp[i][j] = max (dp[i-1 ][j], dp[i][j-1 ])
return dp[-1 ][-1 ]
【3-3】区间DP 模板
def calc () -> int :
n = len ()
dp = [[0 ] * n for _ in range (n+1 )]
for L in range (1 , n+1 ):
for i in range (1 , n+1 ):
j = i + L - 1
for k in range (i+1 , j)
dp[i][j] = max (dp[i][j], dp[i][k] + dp[k][j] + w[i][j])
return dp[1 ][n]
4. 回溯法
回溯算法: ,与 DFS 算法非常类似,本质上就是一种暴力穷举算法。
1 、路径:也就是已经做出的选择。
2 、选择列表:也就是你当前可以做的选择。
3 、结束条件:也就是到达决策树底层,无法再做选择的条件。
result = []
def backtrack (路径, 选择列表 ):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
5. 贪心法
6. 分治法
6.1 二分法
(1)二分查找
二分查找也称折半查找,就是每次查找去掉不符合条件的一半区间,直到找到答案(整数二分)或者和答案十分接近(浮点二分)。
python内置实现二分 (bisection) 算法模块
bisect ,能够 保持序列sequence顺序不变 的情况下对其进行 二分查找和插入,须确保待操作对象是 有序序列,即元素已按 从大到小 / 从小到大 的顺序排列。模块包括函数如下:
bisect_left() 查找 目标元素左侧插入点。存在:第一个相等元素的索引,不存在:第一个更大元素的索引。
bisect_right() 查找 目标元素右侧插入点。存在:最后一个相等元素的索引+1,不存在:第一个更大元素的索引。
bisect() 同 bisect_right()
insort_left() 查找目标元素左侧插入点,并保序地 插入 元素
insort_right() 查找目标元素右侧插入点,并保序地 插入 元素
insort() 同 insort_right()
举例:bisect.bisect_left(a, x, [lo=0, hi=len(a), key=None])
:
在序列 a 中二分查找适合元素 x 插入的位置,保证 a 仍为 有序序列。
若序列 a 中存在与 x 相同的元素,则返回与 x 相同的第一个 (最左侧) 元素的位置索引,使得 x 若插入后能位于其 左侧;
若序列 a 中不存在与 x 相同的元素,则返回与 x 右侧距离最近的元素位置索引,使得 x 若插入后能位于其 左侧。
lo
(包含) 和 hi
(不包含) 为可选参数,分别定义查找范围/返回索引的 上限和下限,缺省时默认对 整个 序列查找。
3.10版本后加入自定义排序参数key
支持。
bisect.bisect
和bisect.bisect_right
返回大于x的第一个下标(相当于C++中的upper_bound
),bisect.bisect_left
返回大于等于x的第一个下标(相当于C++中的lower_bound
)。
(2)二分答案
待求解的最优答案在一个区间内。(一般情况下区间范围很大,暴力超时)
最优答案满足的条件比较复杂,难以直接求解,但是容易判断区间某个答案是否可行。
最优答案的条件对该区间具有单调性:区间中的值越大或越小,条件中某个量对应递增或减少。
问题举例 如:
最短距离最大化问题:条件为保证任意区间距离要比最短距离mid大或相等(这样,mid才是最短距离)即:区间的距离>=mid。目标为求最大值。
最长距离最小化问题:条件为保证任意区间距离要比最大距离mid小或相等(这样,mid才是最大距离)即:区间的距离<=mid。目标为求最小值。
【6-1】二分答案 模板
注意 :可行域为 [left, right]
闭区间,最终答案为 left
或 right
。
def binary_search1 (left, right ):
while left < right:
mid = (left + right) >> 1
if check(mid):
right = mid
else :
left = mid + 1
return left
def binary_search2 (left, right ):
while left < right:
mid = (left + right + 1 ) >> 1
if check(mid):
left = mid
else :
right = mid - 1
return left
def binary_search2 (left, right ):
while right - left > 1e-6 :
mid = (left + right) / 2
if check(mid):
left = mid
else :
right = mid
return left
【6-2】
7. 树 相关
二叉树遍历框架(一般指递归形式)
def traverse (root ):
if root is None :
return
traverse(root.left)
traverse(root.right)
一些规律 :
中序位置主要用在 二叉搜索树BST 场景中,可以把 BST 的中序遍历认为是遍历有序数组。
前序位置的执行是自顶向下的:只能从函数参数中获取父节点传递来的数据,
后序位置的执行是自底向上的:不仅可以获取参数数据,还可以获取到子树通过函数返回值传递回来的数据。
【7-4】字典树 Trie 实现模板
import collections
class TrieNode ():
def __init__ (self ):
self.children = collections.defaultdict(TrieNode)
self.isEnd = False
class Trie ():
def __init__ (self ):
self.root = TrieNode()
def insert (self, word ):
node = self.root
for w in word:
node = node.children[w]
node.isEnd = True
def search (self, word ):
node = self.root
for w in word:
if w not in node.children:
return False
node = node.children[w]
return node.isEnd
def startsWith (self, word ):
current = self.root
for w in word:
current = current.children.get(w)
if current is None :
return False
return True
【7-5】线段树 (查找/更新)实现模板
class SegmentTree :
def __init__ (self, nums ):
self.seg = collections.defaultdict(int )
self.lazy = collections.defaultdict(int )
self.s = 0
self.e = len (nums)-1
def build (idx, s, e, cur_s, cur_e ):
if cur_s > e or cur_e < s:
return
if cur_s == cur_e:
self.seg[idx] = nums[cur_e]
else :
mid = (cur_s + cur_e)//2
build(2 *idx + 1 , s, e, cur_s, mid)
build(2 *idx + 2 , s, e, mid + 1 , cur_e)
self.seg[idx] = max (self.seg[2 *idx + 1 ], self.seg[2 *idx + 2 ])
build(0 , self.s, self.e, self.s, self.e)
def updateLazy (self, idx, val, s, e ):
self.seg[idx] = val
if s != e:
self.lazy[2 *idx + 1 ] = val
self.lazy[2 *idx + 2 ] = val
self.lazy[idx] = 0
def updateRange (self, s, e, val ):
def __updateRange (idx, s, e, val, cur_s, cur_e ):
if cur_s > e or cur_e < s:
return
if self.lazy[idx]:
self.updateLazy(idx, self.lazy[idx], cur_s, cur_e)
if s <= cur_s and cur_e <= e:
self.updateLazy(idx, val, cur_s, cur_e)
else :
mid = (cur_s + cur_e)//2
__updateRange(2 *idx + 1 , s, e, val, cur_s, mid)
__updateRange(2 *idx + 2 , s, e, val, mid + 1 , cur_e)
self.seg[idx] = max (self.seg[2 *idx + 1 ], self.seg[2 *idx + 2 ])
return __updateRange(0 , s, e, val, self.s, self.e)
def queryRange (self, s, e ):
def __queryRange (idx, s, e, cur_s, cur_e ):
if cur_s > e or cur_e < s:
return 0
if self.lazy[idx]:
self.updateLazy(idx, self.lazy[idx], cur_s, cur_e)
if s <= cur_s and cur_e <= e:
return self.seg[idx]
else :
mid = (cur_s + cur_e)//2
left = __queryRange(2 *idx + 1 , s, e, cur_s, mid)
right = __queryRange(2 *idx + 2 , s, e, mid + 1 , cur_e)
return max (left, right)
return __queryRange(0 , s, e, self.s, self.e)
【7-6】树状数组 模板
class FenwickTree :
def __init__ (self, n ):
self.sums_ = [0 ] * (n + 1 )
def update (self, i, delta ):
while i < len (self.sums_):
self.sums_[i] += delta
i += self.lowbit(i)
def query (self, i ):
sum_ = 0
while i > 0 :
sum_ += self.sums_[i]
i -= self.lowbit(i)
return sum_
def lowbit (self, x ):
return x & (-x)
【7-7】 模板
8. 图 相关
【8-0】图 基本算法
graph = defaultdict(list )
in_deg = [0 ] * N
for e in edge_list:
graph[e[0 ]].append(e[1 ])
in_deg[e[1 ]] += 1
from queue import Queue
def topoSort ():
q = Queue()
for i in range (N):
if not deg[i]:
q.put(i)
out = []
while not q.empty():
u = q.get()
out.append(u)
for v in graph[u]:
deg[v] -= 1
if deg[v]==0 : q.put(v)
return out if len (out)==N else []
8.1 最小生成树
【8-1】最小生成树 算法
n = 4
edges = [[0 , 1 , 1 ], [0 , 3 , 3 ], [0 , 2 , 6 ], [2 , 3 , 2 ], [1 , 2 , 4 ]]
parent = list (range (n))
def kruskal ():
cost = 0
for u, v, w in sorted (edges, key=lambda x: x[2 ]):
pu, pv = find(u), find(v)
if pu == pv:
continue
parent[pu] = pv
cost += w
return cost
def prime ():
cost, q, seen = 0 , [], set ()
heappush(q, (0 , 0 ))
for _ in range (n):
w, u = heappop(q)
if u in seen:
continue
cost += w
seen.add(u)
for v, w in graph[u]:
if v in seen:
continue
heappush(q, (w, v))
return cost
8.2 最短路径算法
分类:
单源最短路
所有边权都是正数
朴素的Dijkstra算法 O(n^2) 适合稠密图
堆优化版的Dijkstra算法 O(mlog n)(m是图中节点的个数)适合稀疏图
存在负权边
Bellman-Ford O(nm)
spfa 一般O(m), 最坏O(nm)
多源汇最短路:Floyd算法 O(n^3)
【8-2】单源最短路径 模板
import heapq
def dijkstra (adj, source ):
n = len (adj)
dist = [float ('inf' )] * (n+1 )
prev = [-1 ] * (n+1 )
visited = set ()
dist[source] = dist[0 ] = 0
min_heap = [(0 , source)]
while min_heap:
_, u = heapq.heappop(min_heap)
visited.add(u)
for v in range (n):
if v not in visited and adj[u][v] > 0 :
new_dist = dist[u] + adj[u][v]
if dist[v] > new_dist:
dist[v] = new_dist
prev[v] = u
heapq.heappush(min_heap, (dist[v], v))
return dist
【8-3】多源最短路径 模板
def floyd_warshall (dist, N ):
'''
:param dist: 邻接矩阵 [N, N]
:param N: 节点数
:return: 修改过的邻接矩阵
'''
for k in range (N):
for i in range (N):
for j in range (N):
dist[i][j] = min (dist[i][j], dist[i][k] + dist[k][j])
return dist
8.3 二分图
图论中二分图指的是我们可以将所有节点划分成两个集合,任一集合内部没有边,所有边的起点和终点均在不同集合。
图论中一个重要性质:一个图是二分图当且仅当图中不含奇数环 (环中的边的数量为奇数)。
【8-4】二分图
8.4 网络流
【8-5】网络流
def EK (N, edges, S, T ):
M = len (edges)
graph = [defaultdict(int ) for i in range (N)]
for edge in edges:
graph[edge[0 ]][edge[1 ]] = edge[2 ]
graph[edge[1 ]][edge[0 ]] = -1
flow, pre = [0 ] * N, [0 ] * N
def bfs ():
pre = [-1 ] * len (pre)
q = deque()
q.append(S)
flow[S] = float ('inf' )
while not q.empty():
cur = q.popleft()
if cur == T: break
for to in graph[cur]:
cap = graph[cur][to]
if cap > 0 and pre[to]==-1 :
pre[to] = cur
flow[to] = min (flow[cur], cap)
q.append(to)
return pre[T] != -1
maxflow = 0
while bfs():
maxflow += flow[T]
v = T
while v != S:
graph[v][pre[v]] -= flow[T]
graph[pre[v]][v] += flow[T]
v = pre[v]
return maxflow
现在每条边除了容量外,还有一个属性 单位费用。一条边上的费用等于 流量×单位费用。网络最大流往往可以用多种不同的方式达到,所以现在要求 在保持流最大的同时,找到总费用最少的一种。
只要建了反向边,无论增广的顺序是怎样,都能求出最大流。所以只需要保证任意时刻、对于当前流量flow始终选择最小的费用(求最短路径),这样不断增大流量直至最大,就能得到最小费用最大流。具体地,把EK算法里的BFS换成SPFA:随便找篇
def MCMF (N, edges, S, T ):
M = len (edges)
graph = [defaultdict(int ) for i in range (N)]
for edge in edges:
graph[edge[0 ]][edge[1 ]] = (edge[2 ], edge[3 ])
graph[edge[1 ]][edge[0 ]] = -1
flow, pre, cost = [0 ]*N, [0 ]*N, [0 ]*N
def spfa ():
q = deque()
q.append(S)
pre = [-1 ] * len (pre)
cost = [float ('inf' )] * len (cost)
cost[S] = 0
flow[S] = float ('inf' )
while not q.empty():
cur = q.popleft()
if cur == T: break
for to in graph[cur]:
cap = graph[cur][to][0 ]
cst = graph[cur][to][1 ]
pas = min (flow[cur], cap)
if cap > 0 and cost[to] > cost[cur]+cst*pas:
flow[to] = pas
cost[to] = cost[cur] + flow[to] * cst
if pre[to]==-1 :
pre[to] = cur
q.append(to)
return pre[T] != -1
maxflow, mincost = 0 , 0
while spfa():
maxflow += flow[T]
mincost += cost[T]
v = T
while v != S:
graph[v][pre[v]] -= flow[T]
graph[pre[v]][v] += flow[T]
v = pre[v]
return maxflow
8.5
[End]
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下