关于运输问题的理解这里就不在赘述了,百度上一搜就有很多介绍。
直接进入正题,怎么把算法写出来。这里只考虑运输平衡的运输问题,对应运输不平衡的问题先转换为运输平衡的问题然后解决。
最小元素法需要画一张表,表行标题是对应发货地,列标题是对应收货地点,另外多加一行对应每个收货地的收货量,多加一列对应每个发货地的发货量。
每个格子里有两个数,一个是已知的这个发货地给对应收货地点发货的费用单价dij,另外一个是要求的发货量xij。
| 收货地1 | 收货地2 | 收货地3 | 收货地4 | 总发货量 |
发货1 | 单位运价为 9 | 18 | 1 | 9 | 9 |
发货2 | 11 | 6 | 8 | 18 | 10 |
发货3 | 14 | 12 | 2 | 16 | 6 |
总收货量 | 4 | 9 | 7 | 5 |
|
最小元素法的思想是找到运价最低的一个地方,优先给那里发货。就这么简单
输入:sNum,Rnum, 行数和列数
s[] , r[]总发货量和总收货量
d[][]单位费用数组
输出:
x[] []最后的结果
算法用到辅助变量ok[][] 这个地方已经确定发货量,初始都是0
算法:1)、如果R[j] = 0, 那么ok[k][j] = 1;k = 1, 2, sNum;
如果S[i] = 0, 那么ok[i][k] = 1;k = 1,2,rNum;
2)、如果所有ok[][]都是1 那么Ok计算结束
3)、遍历d[][];找到没有Ok地方里面最小费用的那个行位置和列位置 i, j;
4)、得X[I][J] = min(S[i], R[j]);
S[i] -= X[i][j];
R[j] -= X[i][j];
5)、转1)重新执行。
下面给出代码
typedef std::vector<double> VecDoubleType;
typedef std::vector<bool> VecBoolType;
const double g_dTol = 1.0E-3;
/**********************************
*vecS
*vecE
*vecD
*vecX 最后结果,
***********************************/
static void Cal(VecDoubleType vecS, VecDoubleType vecR
, const VecDoubleType &vecD, VecDoubleType &vecX)
{
const VecDoubleType::size_type iSSize = vecS.size(), iRSize = vecR.size(), iDSize = vecD.size();
assert(iSSize * iRSize == iDSize);
vecX.clear();
VecBoolType vecOk;
// 初始化
VecDoubleType::size_type i = 0, j = 0;
for (i = 0; i != iDSize; i++)
{
vecOk.push_back(false);
vecX.push_back(0.0);
}
while(true)
{
CheakOk(vecS, vecR, vecOk);
if (AllOk(vecOk))
{
break;
}
// 找到最小的距离
VecDoubleType::size_type iMin = iSSize, jMin = iRSize;
double dMinDis = 1.0E7;
for (i = 0; i != iSSize; i++)
{
for (j = 0; j != iRSize; j++)
{
if (!vecOk[i * iRSize +j])
{
if (vecD[i * iRSize + j] - dMinDis < g_dTol)
{
iMin = i;
jMin = j;
dMinDis = vecD[i * iRSize + j];
}
}
}
}
assert(iMin != iSSize && jMin != iRSize);
double dMinItem = min(vecS[iMin], vecR[jMin]);
vecX[iMin * iRSize + jMin] = dMinItem;
vecS[iMin] -= dMinItem;
vecR[jMin] -= dMinItem;
vecOk[iMin * iRSize + jMin] = true;
}
std::vector<VecDoubleType>::size_type iDIndex = 0;
}
static bool AllOk(const VecBoolType &vecOk)
{
bool bAllOk = true;
VecBoolType::size_type iBoolSize = vecOk.size(), i = 0;
for (i = 0; i != iBoolSize; i++)
{
if (!vecOk[i])
{
bAllOk = false;
break;
}
}
return bAllOk;
}
static void CheakOk(const VecDoubleType &vecS, const VecDoubleType &vecR, VecBoolType &vecOk)
{
const VecDoubleType::size_type iSSize = vecS.size(), iRSize = vecR.size();
VecDoubleType::size_type i = 0, j = 0;
// 如果这个发送点的发送量已经为0了,那么这一行都Ok
for (i = 0; i != iSSize; i++)
{
if (vecS[i] < g_dTol)
{
for (j = 0; j != iRSize; j++)
{
vecOk[i * iRSize + j] = true;
}
}
}
// 如果这个接收点的待接收量减为0了,那么这一列都Ok,
for (j = 0; j != iRSize; j++)
{
if (vecR[j] < g_dTol)
{
for (i = 0; i != iSSize; i++)
{
vecOk[i * iRSize + j] = true;
}
}
}
}
测试程序
VecDoubleType vecS, vecR, vecD, vecX;
vecS.push_back(9.0);
vecS.push_back(10.0);
vecS.push_back(6.0);
vecR.push_back(4.0);
vecR.push_back(9.0);
vecR.push_back(7.0);
vecR.push_back(5.0);
vecD.push_back(9.0);
vecD.push_back(18.0);
vecD.push_back(1.0);
vecD.push_back(9.0);
vecD.push_back(11.0);
vecD.push_back(6.0);
vecD.push_back(8.0);
vecD.push_back(18.0);
vecD.push_back(14.0);
vecD.push_back(12.0);
vecD.push_back(2.0);
vecD.push_back(16.0);
Cal(vecS, vecR, vecD, vecX);
// 输入:
// 9 18 1 9 9
// 11 6 8 18 10
// 14 12 2 16 6
//
// 4 9 7 5
//
// 输出为 0,0,7,2,1,9,0,0,3,0,0,3 不是最优,下面的结果 150是最优,
// 3 0 1 5
// 1 9 0 0
// 0 0 6 0
//
//
上面解出的结果不是最优解,只是一个可行解,如果要得到最优解需要继续进行计算,计算方法有位势法和活路法,算法很复杂没有写出来。
但是用另外一种差额法也可以得到基本可行解,而且效果比最小元素法要好。对于上面的列子能直接得到最优解。