1630 字
8 分钟
[Python/数学模型]给大忙人看的速通三——图论优化
目录
最大流问题
输入数据:网络的容量矩阵
变量:每条边的流量
约束条件:流量不能超过边的容量,且满足流量守恒
目标函数:最大化从源点到汇点的流量
输入格式:第1行包含2个整数:n表示农田数量,m表示水管数量。接下来m行,每行包含3个整数:a, b, c,表示从农田a到农田b有一条有向水管,其流量限制为c。农田的编号从1到n。
输出格式:输出一个整数,表示从1号农田到n号农田的最大流量。
输入示例:4 51 2 101 3 52 3 52 4 203 4 5
输出示例:15代码如下:
# 读入n, m = map(int, input().split())edges = []for _ in range(m): a, b, c = map(int, input().split()) edges.append((a, b, c))
# 决策变量:每条边上的流量x = cp.Variable(m)
constraints = []
# 1. 容量约束for i, (a, b, c) in enumerate(edges): constraints.append(x[i] >= 0) constraints.append(x[i] <= c)
# 2. 中间点流量守恒for k in range(2, n): # 2 ~ n-1 inflow = 0 outflow = 0 for i, (a, b, c) in enumerate(edges): if b == k: inflow += x[i] if a == k: outflow += x[i] constraints.append(inflow == outflow)
# 3. 目标:最大化源点1的净流出source_out = 0source_in = 0for i, (a, b, c) in enumerate(edges): if a == 1: source_out += x[i] if b == 1: source_in += x[i]
objective = cp.Maximize(source_out - source_in)
# 求解prob = cp.Problem(objective, constraints)prob.solve()
# 输出结果print(round(prob.value))旅行商问题
输入数据:城市之间的距离矩阵
变量:每条路径是否被选中
约束条件:每个城市只能访问一次,且形成一个闭环
目标函数:最小化总旅行距离
因为旅行商问题的结果是一个闭环,因此我们不需要考虑第一个城市是什么,指定第一个城市作为出发点就可以了。
输入格式:第1行:一个整数 n,表示城市数量。接下来的n行:每行包含n个整数,表示城市之间的距离矩阵。
输出格式:输出一个整数,表示总行程的最短距离。
输入示例:50 1050 1420 1270 4601050 0 1200 240 11001420 1200 0 1950 17601270 240 1950 0 1270460 1100 1760 1270 0
输出示例:4590代码如下:
# ---------- 决策变量 ----------# x[i, j] = 1 表示从 i 到 jx = cp.Variable((n, n), boolean=True)
# MTZ 顺序变量u = cp.Variable(n)
constraints = []
# ---------- 基本约束 ----------# 不允许自己到自己constraints.append(cp.diag(x) == 0)
# 每个城市恰好出发一次for i in range(n): constraints.append(cp.sum(x[i, :]) == 1)
# 每个城市恰好到达一次for j in range(n): constraints.append(cp.sum(x[:, j]) == 1)
# ---------- MTZ 子回路消除 ----------# 固定第0个城市作为起点constraints.append(u[0] == 0)
for i in range(1, n): constraints.append(u[i] >= 1) constraints.append(u[i] <= n - 1)
for i in range(1, n): for j in range(1, n): if i != j: constraints.append(u[i] - u[j] + n * x[i, j] <= n - 1) # 如果选定了 i->j,此时x[i, j] = 1,则 u[i] < u[j]
# ---------- 目标函数 ----------objective = cp.Minimize(cp.sum(cp.multiply(D, x)))最短路径问题
输入数据:图的邻接矩阵和边权
变量:每条边是否被选中
约束条件:形成一条从源点到汇点的路径
目标函数:最小化路径总权重
输入格式:
第1行包含3个整数:n表示城市数量,m表示道路数量,k表示查询数量。
接下来m行,每行包含3个整数:a, b, t,表示城市a和城市b之间有一条双向道路,行驶这条道路需要t时间。城市的编号从1到n。
接下来k行,每行包含2个整数:s, d,表示一次查询,从城市s出发到达城市d。
输出格式:
输出k行,每行包含一个整数,表示对应查询的最短时间。
如果无法到达目标城市,输出-1。
输入示例:4 5 21 2 31 3 12 4 23 4 42 3 51 42 4
输出示例:52代码如下:
input = sys.stdin.readline
def solve_one_query(n, edges, s, t): # 无向边拆成有向边 arcs = [] cost = [] for a, b, w in edges: arcs.append((a, b)) cost.append(w) arcs.append((b, a)) cost.append(w)
m = len(arcs)
# 决策变量:是否选第i条弧 0或者1 x = cp.Variable(m, boolean=True)
constraints = []
# 流量平衡约束 for i in range(1, n + 1): outflow = 0 inflow = 0 for e, (u, v) in enumerate(arcs): if u == i: outflow += x[e] # 这条边被选作路径的起点 if v == i: inflow += x[e] # 这条边被选为路径的终点
if i == s: constraints.append(outflow - inflow == 1) # 起点有后继没有前驱 elif i == t: constraints.append(outflow - inflow == -1) # 终点有前驱没有后继 else: constraints.append(outflow - inflow == 0) # 中间的点必定有前驱和后继
# 目标函数 objective = cp.Minimize(sum(cost[e] * x[e] for e in range(m)))
# 建立问题 prob = cp.Problem(objective, constraints)
# 求解 prob.solve()
return int(round(prob.value))
# 主程序n, m, k = map(int, input().split())edges = []
for _ in range(m): a, b, w = map(int, input().split()) edges.append((a, b, w))
queries = [tuple(map(int, input().split())) for _ in range(k)]
for s, t in queries: print(solve_one_query(n, edges, s, t))最小生成树问题
输入数据:图的邻接矩阵和边权
变量:每条边是否被选中
约束条件:选中的边形成一个连接所有节点的树,并且这个树内部的所有子集的边数都不能超过子集节点数减一
目标函数:最小化选中边的总权重
输入格式:第1行包含2个整数:n表示城市数量,m表示现有道路数量。接下来m行,每行包含3个整数:a, b, c,表示城市a和城市b之间有一条现有道路,升级成本为c。城市的编号从1到n。
输出格式:输出一个整数,表示连接所有城市的最低成本。
输入示例:6 91 2 31 3 11 4 22 3 22 5 33 4 33 6 54 5 65 6 2
输出示例:10代码如下:
import cvxpy as cpimport itertools
n, m = map(int, input().split())
edge = []cost = []
for _ in range(m): a, b, c = map(int, input().split()) edge.append((a, b)) cost.append(c)
x = cp.Variable(m, boolean = True)
constraints = []
constraints.append(cp.sum(x) == n - 1)
nodes = list(range(1, n + 1))
for r in range(2, n): # 子集大小 2 到 n-1 for S in itertools.combinations(nodes, r): S = set(S)
idx = [] for e, (u, v) in enumerate(edge): if u in S and v in S: idx.append(e)
if idx: constraints.append(cp.sum(x[idx]) <= len(S) - 1)
for i in range(m): constraints.append(x[i] <= 1) constraints.append(x[i] >= 0)
objective = cp.Minimize(cp.sum(cp.multiply(cost, x)))
prob = cp.Problem(objective, constraints)prob.solve()
print(int(prob.value)); [Python/数学模型]给大忙人看的速通三——图论优化
https://blog.csdn.net/2501_93882415/article/details/160629160 部分信息可能已经过时











