小区门诊所最佳选址的三种解法

小区门诊所最佳选址的三种解法


文章目录

  • 小区门诊所最佳选址的三种解法
    • 一、问题描述
    • 二、问题求解
    • 三、方法一:0-1变量法
      • 3.1 问题分析
      • 3.2 计算思路
      • 3.3 源代码
      • 3.4 目标函数与决策变量
      • 3.5 整数规划约束条件
      • 3.6 源代码
      • 3.7 结果分析
    • 四、方法二:遍历法
      • 4.1 源代码
    • 五、方法三:邻接矩阵法
      • 5.1 源代码


小区门诊所最佳选址的python求解

一、问题描述

下图给出了7个居民小区,它们的邻接关系及每个小区的居民人数(括号中数字)。限于各方面条件限制,目前暂时只能在其中2个小区开设医疗门诊所,且每个门诊所只服务相邻的两个小区(如在A开设门诊所,可服务于A和B或A和C)。问应在哪两个小区开设门诊所及分别为哪些小区服务,使其覆盖的居民人数最多

表1 各小区的邻接关系及人数
A(3400)
C(4200)
B(2900)
D(2100)
G(7100) F(1800) E(5000)

二、问题求解

5.2的视力看穿答案,在D(或G)开设门诊,并服务于DG;在B(或E)开设门诊,并服务于BE。总服务人数达到最大,为17100人。

可以设置0~1变量,利用Lingo求解。抑或手工计算2^7中情况。

三、方法一:0-1变量法

3.1 问题分析

  1. 线性规划法,7个居民小区应选择4处地点,使得门诊所服务的人数最多,并且每个门诊所只能服务于自身及相邻的小区且只能服务于一个相邻小区,使其覆盖的人数最多。我们可以设置0-1变量,共设置10组0-1变量表示服务于哪两个小区,求解方程组使其目标函数值最大
  2. 遍历法,遍历所有可能的方案,并且计算每一种可能的总覆盖人数,设置标志,排除重复覆盖的情况,得出的所有情况中的最大值即为最佳选址方案。
  3. 邻接矩阵法,构造邻接矩阵并求解。
  4. 不妨假设0-A1-B2-C3-D4-E5-F6-G代替小区编号。

3.2 计算思路

对于决策变量的选取个数及约束条件选择,有两种方法:
(1)枚举法:从A开始遍历直到G结束,列出与之相邻的所有小区组合,即AB、AC、BA 、BC、BD、BE、CACB 、CD、DBDC 、DF、DG、EB 、EF、FDFE 、FG、GDGF ,划去重复的字母组合,即决策变量的个数有:ABACBCBDBECDDFDGEFFG10决策变量(见下图1)。

小区门诊所最佳选址的三种解法_第1张图片
图1

(2)python:首先将与各小区相邻小区的信息写入“字典”存储,接着将各小区的编号存储于“列表”,使用for循环,若满足条件,则写入“字典”,最后输入结果如下(图2)所示。

小区门诊所最佳选址的三种解法_第2张图片
图2

3.3 源代码

'''存储与之相邻元素的信息'''
a=[{
     "no":1,"num":2900},{
     "no":2,"num":4200}]#存储与A相邻的信息,共两个(B和C)
b=[{
     "no":0,"num":3400},{
     "no":2,"num":4200},{
     "no":3,"num":2100},{
     "no":4,"num":5000}]
c=[{
     "no":0,"num":3400},{
     "no":1,"num":2900},{
     "no":3,"num":2100}]
d=[{
     "no":1,"num":2900},{
     "no":2,"num":4200},{
     "no":5,"num":1800},{
     "no":6,"num":7100}]
e=[{
     "no":1,"num":2900},{
     "no":5,"num":1800}]
f=[{
     "no":3,"num":2100},{
     "no":4,"num":5000},{
     "no":6,"num":7100}]
g=[{
     "no":3,"num":2100},{
     "no":5,"num":1800}]
'''信息写入列表'''
c_list=[];c_list.append(a);lstD=[] #存放所有相邻元素的信息的列表
c_list.append(b);c_list.append(c);c_list.append(d)
c_list.append(e);c_list.append(f);c_list.append(g)
stack=[i for i in range(7)]
sum1=0;lst1=[];lst2=[]
for i in range(7):
    for j in range(len(c_list[i])):
        if stack[i]<c_list[i][j]["no"]:
            sum1+=1
            a_dict={
     }
            a_dict["x"]=i
            a_dict["y"]=c_list[i][j]["no"]
            lst1.append(a_dict)
print("变量个数: ",end='');print(sum1)
print("变量索引:(0-A,1-B,2-C,3-D,4-E,5-F,6-G)")
for k in range(len(lst1)):
    print(lst1[k]["x"],end='')
    print(lst1[k]["y"],end=' ')
print()
for k in range(len(lst1)):
    print(chr(65+lst1[k]["x"]),end='')
    print(chr(65+lst1[k]["y"]),end=' ')
print();print("约束条件:")
for i in range(7):
    print("对于",end='');print(chr(65+i),end='')
    print("小区的约束: ",end='')
    for j in range(len(c_list[i])):
        print(chr(65+i),end='')
        print(chr(65+c_list[i][j]["no"]),end=' ')
        if j!=len(c_list[i])-1:
            print("+",end='')
    print("<= 1")

3.4 目标函数与决策变量

决策变量有:

表2 决策变量个数及含义
AB AC BC BD BE CD DF DG EF FG
变量 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10

其中 X 1 ∼ X 10 X_{1}\sim X_{10} X1X10都是 0 − 1 变 量 0-1变量 01,所以该规划的目标函数

M a x Z = 6300 X 1 + 7600 X 2 + 7100 X 3 + 5000 X 4 + 7900 X 5 + 6300 X 6 + 3900 X 7 + 9200 X 8 + 6800 X 9 + 8900 X 10 MaxZ=6300X_{1}+7600X_{2}+7100X_{3}+5000X_{4}+7900X_{5}+6300X_{6}+3900X_{7}+9200X_{8}+6800X_{9}+8900X_{10} MaxZ=6300X1+7600X2+7100X3+5000X4+7900X5+6300X6+3900X7+9200X8+6800X9+8900X10

3.5 整数规划约束条件

因为在两地建门诊所,所以有
∑ i = 1 10 X i = 2 \sum_{i=1}^{10}X_{i}=2 i=110Xi=2
各小区不能重复覆盖两次,即
s . t . { X 1 + X 2 ≤ 1 ; X 1 + X 3 + X 4 + X 5 ≤ 1 X 2 + X 3 + X 6 ≤ 1 X 4 + X 6 + X 7 + X 8 ≤ 1 X 5 + X 9 ≤ 1 X 7 + X 9 + X 10 ≤ 1 X 8 + X 10 ≤ 1 \begin{aligned} s.t.\begin{cases} X_{1}+X_{2}\leq1;\\ X_{1}+X_{3}+X_{4}+X_{5}\leq1\\ X_{2}+X_{3}+X_{6}\leq1\\ X_{4}+X_{6}+X_{7}+X_{8}\leq1\\ X_{5}+X_{9}\leq1\\ X_{7}+X_{9}+X_{10}\leq1\\ X_{8}+X_{10}\leq1 \end{cases} \end{aligned} s.t.X1+X21;X1+X3+X4+X51X2+X3+X61X4+X6+X7+X81X5+X91X7+X9+X101X8+X101
又因为 X 1 ∼ X 10 X_{1}\sim X_{10} X1X10都是 0 − 1 0-1 01变量,所以有
X j = 0 或 1 ( j = 1 , 2 , ⋯   , 10 ) X_{j}=0或1(j=1,2,\cdots,10) Xj=01(j=1,2,,10)

3.6 源代码

 !求解0-1整数规划的小区门诊所最佳选址问题;
max=6300*x1+7600*x2+7100*x3+5000*x4+7900*x5+6300*x6+3900*x7+9200*x8+6800*x9+8900*x10;
x1+x2+x3+x4+x5+x6+x7+x8+x9+x10=2;!两地建门诊所;
x1+x2<=1;!A小区约束条件;
x1+x3+x4+x5<=1;!B小区约束条件;
x2+x3+x6<=1;!C小区约束条件;
x4+x6+x7+x8<=1;!D小区约束条件;
x5+x9<=1;!E小区约束条件;
x7+x9+x10<=1;!F小区约束条件;
x8+x10<=1;!G小区约束条件;
!各决策变量为0-1变量,取值01;
@bin(x1);@bin(x2);@bin(x3);@bin(x4);@bin(x5);
@bin(x6);@bin(x7);@bin(x8);@bin(x9);@bin(x10);

3.7 结果分析

得到Objective value=17100,其中 X 5 X_{5} X5 X 8 X_{8} X8取值为1,其余决策变量的取值为0。所以应该在D(或G)开设门诊,并服务于DG;在B(或E)开设门诊(图3),并服务于BE。即只要使门诊所服务于BEDG即可满足服务人数的最大化,最大服务人数为17100

小区门诊所最佳选址的三种解法_第3张图片
图3

四、方法二:遍历法

4.1 源代码

# -*- coding: utf-8 -*-
from queue import Queue #导入队列的第三方库
q = Queue(maxsize=0) #初始化空队列
'''存储与之相邻元素的信息'''
a=[{
     "no":1,"num":2900},{
     "no":2,"num":4200}]#存储与A相邻的信息,共两个(B和C)
b=[{
     "no":0,"num":3400},{
     "no":2,"num":4200},{
     "no":3,"num":2100},{
     "no":4,"num":5000}]
c=[{
     "no":0,"num":3400},{
     "no":1,"num":2900},{
     "no":3,"num":2100}]
d=[{
     "no":1,"num":2900},{
     "no":2,"num":4200},{
     "no":5,"num":1800},{
     "no":6,"num":7100}]
e=[{
     "no":1,"num":2900},{
     "no":5,"num":1800}]
f=[{
     "no":3,"num":2100},{
     "no":4,"num":5000},{
     "no":6,"num":7100}]
g=[{
     "no":3,"num":2100},{
     "no":5,"num":1800}]
'''信息写入列表'''
c_list=[];c_list.append(a);lstD=[] #存放所有相邻元素的信息的列表
c_list.append(b);c_list.append(c);c_list.append(d)
c_list.append(e);c_list.append(f);c_list.append(g)
b_list=[] #存放所有情况的列表
d_list=[3400,2900,4200,2100,5000,1800,7100] #小区的居民数
number = 0 #覆盖四处地点,计数标志
stack1 = [];stack2 = []
lst = [] #存放一种情况的值
'''数据写入列表'''
for i in range(7):
    for j in range(7):
        if i >= j: #卫生所建在不同的两地
            continue #且只考虑组合,不考虑排列顺序
        else:
            a_list = [] #存储一种建址的情况
            a_dict = {
     } #存储一种建址的信息
            a_dict["x"] = i #一地
            a_dict["y"] = j #二地
            a_list.append(a_dict)
            b_list.append(a_list) #两地选址的情况,不考虑选址顺序
'''判断最大覆盖区域'''
for k in range(len(b_list)):
    # b_list[k][0]["x"]为第k组数据的第一个地址索引
    number = 0 #统计
    stack1 = [];stack2 = []
    flag = [0,0,0,0,0,0,0] #标记,判断是否已覆盖,避免重复,1代表已覆盖
    flag[b_list[k][0]["x"]] = 1 #选中一处地点,标记为1
    flag[b_list[k][0]["y"]] = 1 #选中另一处地点,标记为1
    # 排除相邻的情形
    for m in range(len(c_list[b_list[k][0]["x"]])): #判断这时两者是否相邻
        # c_list[b_list[k][0]["x"]]表示建在某地的索引值
        stack1.append(c_list[b_list[k][0]["x"]][m]) #另一地点相邻的信息
        if c_list[b_list[k][0]["x"]][m]["no"] == b_list[k][0]["y"] and \
           c_list[b_list[k][0]["x"]][m]["num"] == d_list[b_list[k][0]["y"]]:
            stack1.pop() #弹出,不能覆盖
    for n in range(len(c_list[b_list[k][0]["y"]])): #判断这时两者是否相邻
        stack2.append(c_list[b_list[k][0]["y"]][n]) #另一地点相邻的信息
        if c_list[b_list[k][0]["y"]][n]["no"] == b_list[k][0]["x"] and \
           c_list[b_list[k][0]["y"]][n]["num"] == d_list[b_list[k][0]["x"]]:
            stack2.pop() #弹出,不能覆盖
    # 每个地点只能覆盖一次
    for t in range(len(stack1)):
        flag[stack1[t]["no"]]=1
        for r in range(len(stack2)):
            if stack2[r]["no"]==stack1[t]["no"] and \
               stack2[r]["num"]==stack1[t]["num"]:
                continue
            flag[stack2[r]["no"]]=1
            for s in range(len(flag)):
                number = number + flag[s]
                if number==4:
                    break
            z=d_list[b_list[k][0]["x"]]+d_list[b_list[k][0]["y"]]+\
              d_list[stack1[t]["no"]]+d_list[stack2[r]["no"]]
            lst.append(z)
            if z==17100:
                b_dict={
     }
                b_dict["x"]=b_list[k][0]["x"]
                b_dict["y"]=b_list[k][0]["y"]
                b_dict["m"]=stack1[t]["no"]
                b_dict["n"]=stack2[r]["no"]
                lstD.append(b_dict)
print("max=",end='');print(max(lst));
print("索引值:(0-A,1-B,2-C,3-D,4-E,5-F,6-G)")
for k in range(len(lstD)):
    print(lstD[k]["x"],end=' ');print(lstD[k]["y"],end=' ')
    print(lstD[k]["m"],end=' ');print(lstD[k]["n"])

五、方法三:邻接矩阵法

5.1 源代码

clear;clc;
k=[];w=[];
C=[3400,2900,4200,2100,5000,1800,7100];
A=[0 1 1 0 0 0 0;
   1 0 1 1 1 0 0;
   1 1 0 1 0 0 0;
   0 1 1 0 0 1 1;
   0 1 0 0 0 1 0;
   0 0 0 1 1 0 1;
   0 0 0 1 0 1 0;];
%%
for i1=1:7
    for j1=1:7
        if A(i1,j1)==1
            k1=C(i1)+C(j1);
            for i2=1:7
                for j2=1:7
                    if A(i2,j2)==1
                        if (i1~=i2)&&(i1~=j2)&&(j1~=i2)&&(j1~=j2)
                            k2=C(i2)+C(j2);
                            w=[w;i1,j1,i2,j2];
                            k=[k;k1+k2];
                        end
                    end
                end
            end
        end
    end
end
%%
[m,t]=max(k(:));
disp("max=");disp(m);
disp("小区编号");w(t,:)

你可能感兴趣的:(python,列表,算法,matlab,矩阵)