算法设计与分析(期末项目)

本文探讨了Capacitated Facility Location Problem,通过贪心算法和模拟退火(SA)算法寻求解决方案。贪心算法通过两次排序分配顾客,但只能得到局部最优解;模拟退火算法允许接受差解,以跳出局部最优,逼近最优解。通过调整参数和策略,SA通常能获得比贪心算法更好的结果,但计算时间较长。

Capacitated Facility Location Problem


问题描述

Suppose there are n facilities and m customers.We wish to choose:

  • which of the n facilities to open
  • the assignment of customers tofacilities

The objective is to minimize the sum of the opening cost and the assignment cost.

The total demand assigned to a facility must not exceed its capacity.

简而言之就是存在n个工厂,每个工厂有各自的容量上限和开启花费,现在有m个顾客,当一个工厂的剩余容量大于等于某个顾客的需求时,这个工厂才能为这个顾客服务,不同的工厂服务同一个顾客的花费也不同。每个顾客都需要被服务到,怎样安排能使工厂的开启花费和顾客服务花费总和最小。

对于某一个合法的样例来说肯定是存在最优解的,但并不是那么容易求的,老师也不作这方面的要求,只要求用2种不同的方法求好解,因此我选择了最常见的贪心算法和这学期新学的模拟退火(SA)算法,下面我将一一介绍2种方法的实现。

贪心算法

我用的是较为简单的贪心思路,因为有n个工厂,每个顾客自然有n个对应cost,按照某种标准给n个工厂排序,形成第一志愿工厂、第二志愿工厂…起初我是按照顾客在每个工厂的服务花费customer.cost[i]与工厂的开启花费facility[i].cost之和为标准排序,后来经过实验发现,用两个花费的和不如单按照服务花费customer.cost[i]为标准排序求得的结果好。我的猜想是工厂的开启花费往往比服务花费大得多,而且工厂只要开启了就不再需要额外的花费,如果给顾客的服务花费排序时算上工厂开启花费,会对排序结果有较大的影响,差解排在好解前面的概率大大增加。

经过排序后,每个顾客都有了各自的第一志愿工厂,再按照第一志愿工厂的服务花费对顾客进行排序,第一志愿工厂服务花费少的顾客优先分配。分配的原则是如果第一志愿工厂剩余容量足够,就把顾客分配到第一志愿工厂,否则把志愿往后按序推移。

这种贪心的思路总的来说很简单,就是通过两次排序决定顾客和各自志愿工厂优先处理顺序,然后根据排序结果分配。但问题也是显而易见的,就是没有考虑到顾客组合的影响,比如A、B两个顾客占用了工厂1,但其实A、C两个顾客占用工厂1能使花费更少,但却因为C的优先级在B后面,导致A、B分配到工厂1后没有剩余容量能容下C。因此这个贪心算法只能求得局部最优,往往求不到最优解,与最优解的差距和最优解的顾客组合复杂性有关。

测试样例 结果 时间
p1 9472 0.000
p2 8158 0.000
p3 10158 0.000
p4 12158 0.000
p5 9661 0.000
p6 8347 0.000
p7 10347 0.000
p8 12347 0.000
p9 9040 0.000
p10 7726 0.001
p11 9726 0.000
p12 11726 0.000
p13 12032 0.000
p14 12032 0.000
p15 13180 0.000
p16 17180 0.000
p17 12032 0.000
p18 9180 0.000
p19 13180 0.000
p20 17180 0.000
p21 12032 0.000
p22 9180 0.001
p23 13180 0.000
p24 17180 0.000
p25 19189 0.001
p26 16123 0.001
p27 21523 0.001
p28 26923 0.001
p29 19145 0.001
p30 16079 0.002
p31 21479 0.001
p32 26879 0.001
p33 19055 0.001
p34 15989 0.001
p35 21389 0.001
p36 26789 0.001
p37 19055 0.001
p38 15989 0.001
p39 21389 0.001
p40 26789 0.001
p41 7218 0.000
p42 9957 0.000
p43 12448 0.001
p44 7567 0.001
p45 9848 0.000
p46 12639 0.001
p47 6777 0.001
p48 9044 0.001
p49 12420 0.000
p50 10184 0.001
p51 11388 0.000
p52 11966 0.000
p53 13167 0.000
p54 10993 0.000
p55 12013 0.000
p56 23882 0.002
p57 32882 0.001
p58 53882 0.002
p59 39121 0.001
p60 23882 0.001
p61 32882 0.001
p62 53882 0.001
p63 39121 0.001
p64 23882 0.001
p65 32882 0.001
p66 53882 0.001
p67 39671 0.002
p68 23882 0.001
p69 32882 0.002
p70 53882 0.001
p71 39121 0.001
#include <iostream>
#include <fstream>
#include <time.h>

using namespace std;

typedef struct {
   
   
  int capacity; //工厂容量
  int cost; //工厂开启的花费
  int occupy; //当前已经使用的容量
}facility;

typedef struct {
   
   
  int demand; //容量需求
  int *cost;  //对每个工厂的花费
  int *costID;  //记录每个花费对应的是哪个工厂
}customer;

void greedy(string file){
   
   
  //读取文件
  ifstream infile("./Instances/p" + file); 
  ofstream outfile("./Greedy_output", ios::app);
  int facilityNum, customerNum;
  float temp;
  infile >> facilityNum >> customerNum;
  facility *facilities = new facility[facilityNum];
  customer *customers = new customer[customerNum];
  for (int i = 0; i < facilityNum; i++){
   
   
    infile >> facilities[i].capacity >> facilities[i]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值