【数学建模】建筑工地开工问题

题目:

某公司有 6 6 6个建筑工地要开工,每个工地的位置(用平面坐标 ( a , b ) (a,b) (a,b)表示,距离单位: k m km km)及水泥日用量 d ( 单位 : t ) d(单位:t) d(单位:t)由下表给出,目前有两个临时料场位于 P ( 5 , 1 ) , Q ( 2 , 7 ) P(5,1),Q(2,7) P(5,1),Q(2,7),日储量各有 20 t 20t 20t。

工地 1 2 3 4 5 6
a 1.25 8.75 0.5 5.75 3 7.25
b 1.25 0.75 4.75 5 6.5 7.75
d 3 5 4 7 6 11

研究下列问题:

1)假设从料场到工地之间均有直线道路相连,试制定每天的供应计划,即从每个料场分别向各工地运送多少吨水泥,使总的吨公里数最小。

首先我们要知道吨公里数是什么:

设运送水泥为 x x x吨,运送了 y y y公里,那么吨公里数就是 x y xy xy

所以我们可以设每次运输水泥为 x i x_i xi吨,运送了 y i y_i yi公里,那么总的吨公里数最小模型 就是
min ⁡ ∑ i ∈ N ( x i y i ) \min{\sum_{ i\in N }(x_iy_i)} min∑i∈N(xiyi)

建立供应条件模型:

设料场 i i i到工地 j j j的运输量为 X i j X_{ij} Xij

则所有料场向某工地运输量之和大于等于该工地水泥日用量 d i d_i di:
∑ j ∈ N X i j > = d i , i ∈ N \sum_{ j\in N }X_{ij} >= d_i, i\in N ∑j∈NXij>=di,i∈N

且某料场对所有工地运算量之和不得超过料场的日储量 e j e_j ej:
∑ i ∈ N X i j ≤ e j , j ∈ N \sum_{ i\in N }X_{ij} \le e_j, j\in N ∑i∈NXij≤ej,j∈N

最后建立料场和工地距离模型 :

设料场 P ( x , y ) P(x,y) P(x,y)到工地 A ( a , b ) A(a,b) A(a,b)的运输量为 d i s P A dis_{PA} disPA

则: d i s P A = ( x − a ) 2 + ( y − b ) 2 dis_{PA} = \sqrt{(x-a)^2+(y-b)^2} disPA=(x−a)2+(y−b)2

已知有6个工地和2个料场

将料场和工地距离模型和供应条件模型带入吨公里数最小模型可得:
a n s = min ⁡ ∑ i ∈ N ( X i j d i s i j ) , j = 1 , 2 ans = \min{\sum_{ i\in N }(X_{ij}dis_{ij})},j=1,2 ans=min∑i∈N(Xijdisij),j=1,2

或者
a n s = min ⁡ ∑ j = 1 , 2 ∑ i ∈ N , i < = 6 ( X i j d i s i j ) ans = \min{\sum_{j=1,2 }\sum_{ i\in N ,i<=6 }(X_{ij}dis_{ij})} ans=min∑j=1,2∑i∈N,i<=6(Xijdisij)

建模后带入已知进行LINGO求解:

bash 复制代码
sets:
 aa/1..6/:a,b,d;
 bb/1..2/:e,x,y;
 cc(aa,bb):k;
endsets
data:
 a = 1.25,8.75,0.5,5.75,3,7.25;
 b = 1.25,0.75,4.75,5,6.5,7.75;
 d = 3,5,4,7,6,11;
 e = 20,20;
 x = 5,2;
 y = 1,7;
enddata
min = @sum(cc(i,j):k(i,j)*@sqrt((a(i)-x(j))^2 + (b(i)-y(j))^2));
@for(aa(i):@sum(bb(j):k(i,j))=d(i));
@for(bb(j):@sum(aa(i):k(i,j))<=e(j));

解出:

bash 复制代码
Objective value:                              136.2275
                       K( 1, 1)        3.000000            0.000000
                       K( 1, 2)        0.000000            3.852207
                       K( 2, 1)        5.000000            0.000000
                       K( 2, 2)        0.000000            7.252685
                       K( 3, 1)        0.000000            1.341700
                       K( 3, 2)        4.000000            0.000000
                       K( 4, 1)        7.000000            0.000000
                       K( 4, 2)        0.000000            1.992119
                       K( 5, 1)        0.000000            2.922492
                       K( 5, 2)        6.000000            0.000000
                       K( 6, 1)        1.000000            0.000000
                       K( 6, 2)        10.00000            0.000000

如果工地到工地之间也有道路连接

假设每个工地都是一个点,那么这个点送进的水泥数量 i n i in_i ini和送出的水泥数量 o u t i out_i outi需要满足 i n i − o u t i > = d i in_i - out_i >= d_i ini−outi>=di , d i d_i di为该点水泥日用量

如果把料场也算进去,那么就需要设料场的 d d d为0即可,即料场可以不留任何水泥

将所有的道路编号 z = 1 , 2 , 3 , 4... z=1,2,3,4... z=1,2,3,4...,设改道路

编程求解

思路1:

化成图 , 遍历每个工厂,找到工厂到料场的最短距离(bfs),用这个最短距离替换料场和工地距离再带入上面问题模型求解即可:

C++:

cpp 复制代码
#include <iostream>
#include <cmath>
#include <queue>
using namespace  std;
double a[10] = {0, 1.25,8.75,0.5,5.75,3,7.25,5,2},b[10] = {0, 1.25,0.75,4.75,5,6.5,7.75,1,7};
double px = 5 , py = 1;
double qx = 2 , qy = 7;
double dis[10][10];
double min_dis[10]; // p
double min_dis2[10]; // q

void bfs(int begin){
    queue<int>q;
    q.push(begin);
    while(!q.empty()){
        int x = q.front();q.pop();
        for(int i=1;i<=8;i++){
            if(begin == 7){
                if(min_dis[i] > min_dis[x] + dis[x][i]){
                    min_dis[i] = min_dis[x] + dis[x][i];
                    q.push(i);
                }
            }
            else{
                if(min_dis2[i] > min_dis2[x] + dis[x][i]){
                    min_dis2[i] = min_dis2[x] + dis[x][i];
                    q.push(i);
                }
            }
        }
    }
}


int main() {
    //init dis
    for(int i=1;i<=8;i++){
        for(int j=i+1;j<=8;j++){
            dis[i][j] = dis[j][i] = sqrt((a[i]-a[j]) * (a[i]-a[j])+  (b[i]-b[j])*(b[i]-b[j])); // 初始化距离
        }
    }
    for(int i=1;i<=6;i++)min_dis[i] = min_dis2[i] = 0x3f3f3f3f; // max set
    min_dis[7] = min_dis2[8] = 0;
    bfs(7); // p
    bfs(8); // q
    for(int i=1;i<=6;i++){
        cout << min_dis[i] << ' ';
        
    }cout << '\n';
    for(int i=1;i<=6;i++){
        cout << min_dis2[i] << ' ';

    }cout << '\n';
    return 0;
}

求解得

cpp 复制代码
3.75832 3.75832 5.85769 4.06971 5.85235 7.11512
5.79871 9.19918 2.70416 4.25 1.11803 5.3033

或者:

cpp 复制代码
for(int i=1;i<=6;i++){
        cout << min_dis[i] << ',';
        cout << min_dis2[i] << ',';
    }

求解得

cpp 复制代码
3.75832,5.79871,3.75832,9.19918,5.85769,2.70416,4.06971,4.25,5.85235,1.11803,7.11512,5.3033,
bash 复制代码
sets:
 aa/1..6/:d;
 bb/1..2/:e;
 cc(bb,aa):k,dis;
endsets
data:
 d = 3,5,4,7,6,11;
 e = 20,20;
 dis= 3.75832,3.75832,5.85769,4.06971,5.85235,7.11512,5.79871,9.19918,2.70416,4.25,1.11803,5.3033;
enddata
min = @sum(cc(i,j):k(i,j)*dis(i,j));
@for(aa(i):@sum(bb(j):k(j,i))=d(i));
@for(bb(j):@sum(aa(i):k(j,i))<=e(j));
bash 复制代码
  Objective value:                              136.2275
  Infeasibilities:                              0.000000

2)为了进一步减少吨公里数,打算舍弃目前的两个临时料场,改建两个新的临时料场日储量还是20t,给出新料场的位置。

直接用第一问的模型:
a n s = min ⁡ ∑ j = 1 , 2 ∑ i ∈ N , i < = 6 ( X i j d i s i j ) ans = \min{\sum_{j=1,2 }\sum_{ i\in N ,i<=6 }(X_{ij}dis_{ij})} ans=min∑j=1,2∑i∈N,i<=6(Xijdisij)

设新料场坐标为 P ( x 1 , y 2 ) P(x_1,y_2) P(x1,y2)和 Q ( x 2 , y 2 ) Q(x_2,y_2) Q(x2,y2)

则:
a n s = min ⁡ ∑ j = 1 , 2 ∑ i ∈ N , i < = 6 X i j ( x j − a i ) 2 + ( y j − b i ) 2 ans = \min{\sum_{j=1,2 }\sum_{ i\in N ,i<=6 }X_{ij} \sqrt{(x_j-a_i)^2+(y_j-b_i)^2}} ans=min∑j=1,2∑i∈N,i<=6Xij(xj−ai)2+(yj−bi)2

如果直接带入LINGO求解,实际上就是问题一的答案去掉 x , y x,y x,y的复制

bash 复制代码
sets:
 aa/1..6/:a,b,d;
 bb/1..2/:e,x,y;
 cc(aa,bb):k;
endsets
data:
 a = 1.25,8.75,0.5,5.75,3,7.25;
 b = 1.25,0.75,4.75,5,6.5,7.75;
 d = 3,5,4,7,6,11;
 e = 20,20;
 !x = 5,2;
 !y = 1,7;
enddata
min = @sum(cc(i,j):k(i,j)*@sqrt((a(i)-x(j))^2 + (b(i)-y(j))^2));
@for(aa(i):@sum(bb(j):k(i,j))=d(i));
@for(bb(j):@sum(aa(i):k(i,j))<=e(j));

如果赋初值给 x , y x,y x,y

bash 复制代码
sets:
 aa/1..6/:a,b,d;
 bb/1..2/:e,x,y;
 cc(aa,bb):k;
endsets
data:
 a = 1.25,8.75,0.5,5.75,3,7.25;
 b = 1.25,0.75,4.75,5,6.5,7.75;
 d = 3,5,4,7,6,11;
 e = 20,20;

enddata
init:
 x = 5,2;
 y = 1,7;
endinit
min = @sum(cc(i,j):k(i,j)*@sqrt((a(i)-x(j))^2 + (b(i)-y(j))^2));
@for(aa(i):@sum(bb(j):k(i,j))=d(i));
@for(bb(j):@sum(aa(i):k(i,j))<=e(j));

MATLAB求解看数学规划模型(2)-非线性规划

相关推荐
hansang_IR13 小时前
【代数与组合数学 | 那忘算 5】生成函数 & 例题 & 卷积
c++·算法·多项式·生成函数·母函数
Zane199413 小时前
快速排序凭什么叫"快"排序?平均O(nlogn)背后,藏着一个能让它退化成O(n²)的选择
算法
无忧.芙桃13 小时前
数据结构之排序算法(上):从评价指标到插入、希尔、选择与堆排序
c语言·c++·排序算法
syagain_zsx13 小时前
算法基础篇 · 02 高精度(C++ 题解)
开发语言·c++·学习·高精度
6Hzlia13 小时前
【Classic 150 刷题计划】 LeetCode 242. 有效的字母异位词 | C++ 哈希计数与严密防线
c++·算法·leetcode
wabs66614 小时前
关于二叉树【力扣101.对称二叉树的思考】
数据结构·c++·算法·leetcode·二叉树
6Hzlia14 小时前
【Classic 150 刷题计划】 LeetCode 228. 汇总区间 | C++ 双游标区间扫描与 to_string 规范
c++·算法·leetcode
玖玥拾15 小时前
LeetCode 383 赎金信
算法·leetcode
维克兜率天15 小时前
5.3 宏观因子:抬头看天
笔记·python·深度学习·算法·量化
LuminousCPP15 小时前
数据结构-排序(五):计数排序与排序专题阶段总结|从外部归并到线性排序,再看算法边界与选型
c语言·数据结构·笔记·算法·排序算法