用分支与限界策略求解货物调运问题

引入

3、用分支与限界策略求解货物调运问题
某大型集团公司在全国有m (1 < m 20) 个产品生产基地(通称产地),用Ai 表示(i = 1, 2, …, m),需要分别将这些物质调运到n (1 < n 20) 个集中消费的地区(通称销地),用Bj 表示(j = 1, 2, …, n)。
已知这m 个产地的产量分别为a1, a2, …, am(简写为ai),n 个销地的销量分别为b1, b2, …, bn(简写为bj),从第i 个产地到第j 个销地的单位货运价格为cij,并且总产量和总销量不一定相等。
应当如何制定调运方案,在尽可能地满足销量的前提下,使得总的运输费用为最小。
例:某集团公司的产量和销量以及单位运价表(千元/吨)
在这里插入图片描述
其总运费最小的调运方案为:
在这里插入图片描述
运费总计:5 3 + 2 10 + 3 1 + 1 8 + 6 4 + 3 5 = 85(千元)。

思路

这类运输问题要分为两种情况讨论:
1当a[]=b[]时,运输问题的总产量等于其总销量时,这样的运输问题称为产销平衡的运输问题
2当a[]/=b[]时,运输问题的总产量不等于其总销量时,这样的运输问题称为产销不平衡的运输问题

数学模型

产销平衡条件

用Xij表示从A j 到Bj 的总运量,C[i][j]*X[i][j]就表示从第i 个产地到第j 个销地的总价格,即数学模型为:
m i n Z = ∑ i = 1 m ∑ j = 1 n C i j X i j min Z=\sum_{i=1} ^m\sum_{j=1} ^n C_{ij} X_{ij} minZ=i=1mj=1nCijXij

s . t . { ∑ i = 1 m x i j = b j , j = 1 , 2 , . . . , n ∑ j = 1 n x i j = a i , i = 1 , 2 , . . . , m x i j > = 0 s.t. \begin{cases} \sum \limits_{i =1}^m x_{ij}=b_j , & j=1,2,...,n \\\\ \sum_{j=1}^n x_{ij}=a_i , & i=1,2,...,m \\\\ x_{ij}>=0 \\ \end{cases} s.t.i=1mxij=bj,j=1nxij=ai,xij>=0j=1,2,...,ni=1,2,...,m
其中,ai和bj满足: ∑ i = 1 m a i = ∑ j = 1 n b j \sum\limits_{i=1}^m a_i=\sum\limits_{j=1}^n b_j i=1mai=j=1nbj 称为产销平衡条件。

产销不平衡条件

产量大于销量
用Xij表示从A j 到Bj 的总运量,C[i][j]*X[i][j]就表示从第i 个产地到第j 个销地的总价格,即数学模型为:
m i n Z = ∑ i = 1 m ∑ j = 1 n C i j X i j min Z=\sum_{i=1} ^m\sum_{j=1} ^n C_{ij} X_{ij} minZ=i=1mj=1nCijXij

s . t . { ∑ j = 1 n x i j < = a i , i = 1 , 2 , . . . , m ∑ i = 1 m x i j = b j , j = 1 , 2 , . . . , n x i j > = 0 , i = 1 , 2 , . . . , m j = 1 , 2 , . . , n s.t. \begin{cases} \sum \limits_{j =1}^n x_{ij}<=a_i , & i=1,2,...,m \\\\ \sum\limits_{i=1}^m x_{ij}=b_j , & j=1,2,...,n \\\\ x_{ij}>=0 ,& i=1,2,...,m\quad j=1,2,..,n\\ \end{cases} s.t.j=1nxij<=ai,i=1mxij=bj,xij>=0,i=1,2,...,mj=1,2,...,ni=1,2,...,mj=1,2,..,n

解法:增加一个假想的销地 B n + 1 B_{n+1} Bn+1,其销量为: b n + 1 = ∑ i = 1 m a i − ∑ j = 1 n b j b_{n+1}=\sum\limits_{i=1}^m a_i -\sum\limits_{j=1}^n b_j bn+1=i=1maij=1nbj
数学模型就改为了 m i n Z = ∑ i = 1 m ∑ j = 1 n + 1 C i j X i j min Z=\sum\limits_{i=1} ^m\sum\limits_{j=1} ^{n+1} C_{ij} X_{ij} minZ=i=1mj=1n+1CijXij

产量小于销量
和上面的解法类似,增加一个假想的产地 A m + 1 A_{m+1} Am+1,起产量为: a m + 1 = ∑ j = 1 n b j − ∑ i = 1 m a i a_{m+1}=\sum\limits_{j=1}^n b_j -\sum\limits_{i=1}^m a_i am+1=j=1nbji=1mai
就将不平衡问题转化为了平衡问题 m i n Z = ∑ i = 1 m + 1 ∑ j = 1 n C i j X i j min Z=\sum\limits_{i=1} ^{m+1}\sum\limits_{j=1} ^{n} C_{ij} X_{ij} minZ=i=1m+1j=1nCijXij


表上作业法

在讲解上别人用的表上作业法,先确定一个可行解,然后检验当前可行方案是否最优,采用闭回路法从当前方案出发寻找更好的方案。
可行解是一定存在的,可以用最小元素法(贪心法求解),西北角法,Vogel法。
我用的最小元素法求的解这里只说明这一种求解思路,其他方案大家可以看看下方我给的链接。

最小元素法求可行解

基本思路是优先满足单位运价最小的供销业务。首先找出运价最小的,并以最大限度满足其供销量为原则确定供销业务。同样的方法反复进行直到确定了所有的供销业务,得到一个完整的调运方案跳出,即找到一组可行解。
在这里插入图片描述
在这里插入图片描述

闭回路法检验可行解是否为最优解

求出每个空格位置的检验数,当所有的检验数都大于或等于零的时候该调运方案就是最优方案。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


使用分支限界法求解运输问题

理解了上述表上作业法求解运输问题之后,就可以根据表上作业法的思路,找出关系函数去限定上下界,加快判断当前方案是否为最优的节奏。
数学模型和上面的一样 m i n Z = ∑ i = 1 m ∑ j = 1 n C i j X i j min Z=\sum_{i=1} ^m\sum_{j=1} ^n C_{ij} X_{ij} minZ=i=1mj=1nCijXij
利用贪心法求一个可行的近似解,然后用广度优先遍历,找出最佳解出队

代码

#include <iostream> 
#include <queue> 
#include<limits>
#define min(x,y) (x<y?x:y) 
using namespace std; 
//存放运费
int freight[3][4] = { {3,11,3,10},{1,9,2,8},{7,4,10,5} };
//存放销售价格
int sales[4] = { 3,6,5,6 };
//存放产量
int production[3] = { 7,4,9 }; 
//记录最小运输费用
int mincost =INT_MAX;
//闭回路法所需数组,探寻周围空格是否存在回路
int key[5][3] = { {1,3,2},{2,1,3},{2,3,1},{3,2,1},{3,1,2} };

//为了方便,将查询出来的可行解放到一个结构体中
struct NodeType 
{ 
	int r[4];
	int lb; 
	int pro[3][4] = { 0 };
	bool operator <(const NodeType& s) const 
	{
	return  s.lb<lb;
	} 
};

//分支回溯的复原函数
void ret() {
	sales[0] = 3, sales[1] = 6, sales[2] = 5, sales[3] = 6;
	production[0] = 7, production[1] = 4, production[2] = 9; 
} 

//分支限界的约束函数
void bound(NodeType& e) { 
	int sum = 0, j;
	for (int m = 0; m < 4; m++) { //先用贪心法求一个可行解
		j = e.r[m]; 
		int temp, i = 0;
		//min为最大小界,max为最小上界
		int min = 100, max = 0, x[3] = { 0 };
		for (int k = 0; k < 3; k++) { 
			if (min > freight[k][j]) {
				x[0] = k;
				min = freight[k][j]; 
			} 
		}
		for (int k = 0; k < 3; k++) {
			if (max < freight[k][j]) { 
				x[2] = k; 
				max = freight[k][j]; 
			}
		}
		x[1] = 3 - x[0] - x[2];
		while (sales[j] > 0) { 
			temp = sales[j];
			e.pro[x[i]][j] = min(sales[j], production[x[i]]);
			sales[j] -= production[x[i]];
			if (sales[j] > 0) {
				sum = sum + production[x[i]] * freight[x[i]][j]; 
				production[x[i]] = 0; i++; 
			} else { 
				sales[j] = 0;
				sum = sum + freight[x[i]][j] * temp;
				production[x[i]] -= temp; 
			} 
		} 
	} 
	e.lb = sum; //再将上界赋给可行解的结构体中
} 

//广度优先遍历,找出最佳解
void bfs() 
{
	priority_queue<NodeType> qu;
	NodeType e, e1;
	e.r[4] = { 0 };
	for (int m = 0; m < 4; m++)
		e.r[m] = m;
	bound(e);//求出一个可行解,然后进行检验
	ret();
	qu.push(e); 
	int j = 0;
	while (!qu.empty()) {
		e = qu.top();
		qu.pop();
		if (mincost > e.lb)
		{
			mincost = e.lb; 
		}
		e1.r[0] = e.r[0];
		//闭回路法求解检验方案是否为最优
		for (int k = 1; k < 4; k++) { 
			e1.r[k] = key[j][k - 1];
		}
		j++;
		bound(e1);//在循环中,求出一个可行解,然后进行检验
		ret();
		if (e1.lb <= mincost) 
			qu.push(e1);
	} 
	//队列为空之后,全部出队保存在pro中值就是最佳解
	cout << "总运费最小的调运方案为:" << endl;
	cout << "\tB1\tB2\tB3\tB4" << endl;
	cout << endl;
	for (int i = 0; i < 3; i++) { 
		cout << "A" << i + 1 << "\t";
		for (int j = 0; j < 4; j++) 
		{ 
			cout << e.pro[i][j] << "\t"; 
		} 
		cout << endl; 
		cout << endl; 
	} 
	cout << endl;
}


int main() 
{
	bfs();
	cout << "\t 运输的最小费用为:"; 
	cout << mincost << endl;
	return 0; 
}

参考

在豆丁上有讲具体算法思路的,这一题我自己琢磨了三天都没搞明白,看别人详解数学模型才看懂的,学都可以学~
https://www.docin.com/p-624702741.html

  • 9
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AlbertOS

还会有大爷会打钱?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值