问题:已知某种商品m个仓库的存货量,n个客户对该商品的需求量,单位商品运价。试确定m个仓库到n个客户的商品调运数量,使总的运输费用最小。
输入格式:
第一行两个数m,n
第2行到第m+1行 对应商品运价
第m+2行对应需求量
第m+3行对应存货量
输出格式:
精确到小数点后两位
输入样例:
在这里给出一组输入。例如:
6 8
6 2 6 7 4 2 5 9
4 9 5 3 8 5 8 2
5 2 1 9 7 4 3 3
7 6 7 3 9 2 7 1
2 3 9 5 7 2 6 5
5 5 2 2 8 1 4 3
35 37 22 32 41 32 43 38
60 55 51 43 41 52
输出样例:
在这里给出相应的输出。例如:
664.00
我们将这个问题分为两个模块,第一部分是很简单的输入数据部分,第二部分就是基于cvxpy的线性规划问题。
一、数据输入(这里的操作基本都差不多,所以放在一起讲)
import cvxpy as cp
import numpy as np
m, n = [eval(x) for x in input().split()]
c = np.zeros((m, n))
for i in range(m):
c[i, :] = [eval(x) for x in input().split()]
demand = [eval(x) for x in input().split()] # demand 代表需求量
inventory = [eval(x) for x in input().split()] # inventory代表存货量
input.split()函数将我们输入的数据按空格进行分隔,并返回分割后的字符串列表。再调用for循环并通过eval函数将列表里的每个str改为int,再分别给m和n赋值。
c=np.zeros((m,n))创建了一个元素全为0的m*n的矩阵,然后通过for循环和c[i,:]=[eval(x) for x in input().split()]给矩阵c里的第i行元素赋值。
下面的demand和inventory分别代表客户需求量和仓库存货量。
二、cvxpy线性规划
x = cp.Variable((m, n))
obj = cp.Minimize(cp.sum(cp.multiply(c, x)))
con1 = cp.sum(x, axis=1) <= inventory
con2 = cp.sum(x, axis=0) == demand
con3 = x >= 0
cons = [con1, con2, con3]
prob = cp.Problem(obj, cons)
prob.solve()
print("{0:.2f}".format(prob.value), end='')
cvxpy库使用较难,相关资料也比较难理解,下面是(cvxpy库的官方介绍),本人使用的是Pycharm2022,在安装cvxpy库过程中曾经遇到过安装卡顿问题,可以通过更换镜像源的方法解决(2022 Pycharm 不同版本镜像源添加)。
首先我们要明白使用cvxpy库的三要素——需求变量、目标函数、约束条件。
①需求变量
在线性规划运输问题中,我们最终要求解的是最小的总运输费用,因为从每个仓库到每个客户的单位运价已经给出,所以这里我们要先构造一个同样为m*n的矩阵来存储最终解矩阵(为使总运输费用最小每个仓库对每个客户的商品供给量)
x = cp.Variable((m, n))
生成了m行n列的矩阵,且为变量(可以理解为创建了一个全为变量的矩阵)
上述过程可以理解为设未知数求解方程
obj = cp.Minimize(cp.sum(cp.multiply(c, x)))
②目标函数
cp.multiply函数将单位运价矩阵c和解矩阵x相乘(这里的乘是对应位置元素相乘),得到的结果是运输费用矩阵。
multipy和*含义一样,前者是函数,后者是运算符号,含义是矩阵元素对应相乘,
而dot和@含义一样,前者是函数,后者是运算符号,含义是线性代数中的矩阵乘法。
这里的cp.sum()是将矩阵中所有的元素进行求和,再调用Minimize函数得到的结果就是最终要求的最小总运输费用。
sum函数用法为:当不含axis参数时,sum(a)是将矩阵a中所有元素进行求和。而当含有axis参数时,sum(a,axis=0/1),axis=0,对于多维矩阵来说,是将每一个列向量相加,而当axis=1时,是将每一个行向量相加(详见sum函数用法)。
③约束条件
我们在上述过程中创建的x矩阵为m*n的最终解矩阵(为使总运输费用最小每个仓库对每个客户的商品供给量),需对其添加约束条件。
con1 = cp.sum(x, axis=1) <= inventory con2 = cp.sum(x, axis=0) == demand con3 = x >= 0 cons = [con1, con2, con3]
con1=cp.sum(x,axis=1)<=inventory将解矩阵中每一个行向量相加,每一个对应的和都要小于等于inventory(存货量)中对应位置的存货量。
con2=cp.sum(x,axis=0)=demand将解矩阵中每一个列向量相加,每一个对应的和都要等于demand(需求量)中对应位置的需求量。
con3=x>=0解矩阵中每一元素都要大于等于0。
prob = cp.Problem(obj, cons) prob.solve() print("{0:.2f}".format(prob.value), end='')
最后调用cp.Problem函数,obj为目标函数,cons为约束条件,构建prob,并通过prob.solve()求解。
得到的输出结果如下
注意这里最终的输出要是prob.value
我们再看一下求解过程中要用的解矩阵x的值print(x.value)
完整代码如下:
import cvxpy as cp
import numpy as np
m, n = [eval(x) for x in input().split()]
c = np.zeros((m, n))
for i in range(m):
c[i, :] = [eval(x) for x in input().split()]
demand = [eval(x) for x in input().split()] # demand 代表需求量
inventory = [eval(x) for x in input().split()] # inventory代表存货量
x = cp.Variable((m, n))
obj = cp.Minimize(cp.sum(cp.multiply(c, x)))
con1 = cp.sum(x, axis=1) <= inventory
con2 = cp.sum(x, axis=0) == demand
con3 = x >= 0
cons = [con1, con2, con3]
prob = cp.Problem(obj, cons)
prob.solve()
print("{0:.2f}".format(prob.value), end='')