目录
1,问题
1.题目描述
某电商的某个商品在编号为0的城市生产,有编号为1~N的N个城市购买了该商品,购买商品的数量分别为Qi,现在需要将商品从城市0运送到这N个城市。已知0~N之间城市i到城市j的距离为Dij(1≤Dij≤5000)公里,电商有足够多的车可运送该商品,因超载等限制,每辆车最多可配送的商品数量为2000,车的行进速度和所运送商品数量有关系,见表1所示。请帮忙规划路线,使所有运送车的运送时间总和最小(注:时间以分钟计算,所有计算均采用向上取整的方式)。
表1:行进速度表
| 所运送商品数量 |
速度(公里/小时) |
| [1500, 2000] |
60 |
| [1200, 1500) |
70 |
| [800, 1200) |
80 |
| [500, 800) |
90 |
| [0, 500) |
100 |
2.约束条件
0号城市和其他N个城市不在一起;
车辆均需从城市0出发,最终返回城市0,中途不能经过城市0;
每个城市的需求数量必须全部满足,且一个城市只能接受一辆车的一次配送和经过;
车的数量足够多,由参赛选手规划车辆数量;
每辆车均不允许超载;
两两城市均联通,城市A到B的距离和B到A的距离可能不同;
忽略其他题目未描述的时间消耗,包括交通堵塞时间,加油时间,人工休息时间,卸货时间等均不计算。
3.题目输入
输入以文件“delivery.txt”给出。文档中将包含多个测试用例,每个用例由一组数据组成:
第一行包含一个整数N表示需要商品的城市个数,其中(1≤N≤100)。
然后是N+1行,每行含有N+1个整数,即N+1矩阵,表示N+1个城市两两之间的距离,Dij表示城市i到城市j的距离。
然后再是N行,表示每个城市需要的商品G的数量为Qi(0<Qi≤2000)。
数据样例:
delivery.txt
2
0, 60, 120
60, 0, 40
90, 50, 0
200
700
3
0, 100, 200, 180
100, 0, 150, 220
200, 150, 0, 120
180, 220, 120, 0
600
900
1200
4.答案提交
编写完整程序,输出“answer.txt”文件。
“answer.txt”文件中每行数据表示一个用例的结果,为(运送时间总和:车辆1装货数量:路径1; 车辆2装货数量:路径2;…;车辆K装货数量:路径K)
每行答案数据以“(”起始,以“)”终止。运送时间总和和后续内容以“:”分隔,装货数量和路径以“:”分隔,路径中各城市编号以“,”分隔,中间只可以出现0个或多个空格“ ”,多辆车的路径间以“;”分隔,所有符号均为英文字符,不允许出现其他非法字符。
答案示例:
(126:900:0,1,2,0)
(596:1500:0,1,2,0;1200:0,3,0)
5.评分规则
所有用例的路径的运送时间总和短者胜出。
如果时间相同,程序运行时间短者胜出。
如果程序时间也相同,先提交代码的队伍胜出。
2,基础代码
启发式分组:
基于打分机制,选择哪个城市该接到当前城市后面
b接在a之后的必要性 = min(x,b)-(a,b) + min(a,y)-(a,b),b的范围包括0,即其他城市的必要性如果都小于0就直接从a回0了,但是什么时候应该返回0需要在getEmerg里面特判
min(x,b)-(a,b) + min(a,y)-(a,b),x和y的范围包括0,所以getMinLeft和getMinRight的循环应该从0开始
#define windows
#include<iostream>
#include<string>
#include<vector>
#include<time.h>
#include<functional>
#include "csimsgeek.h"
using namespace std;
#define CIN(x) while(!(cin>>x)){cin.clear();cin.ignore();}
#define SIZE 2000
int N;//城市0,1—N
vector<vector<int>>len;//两两之间的距离
int q[101];//每个城市的需求量
int visit[101];//访问标记
int init()
{
if(!(cin>>N))return 0;//EOF
len.resize(N+1);
for(int i=0;i<=N;i++)
{
len[i].resize(N+1);
for(int j=0;j<=N;j++)
{
CIN(len[i][j]);
}
visit[i]=0;
}
for(int i=1;i<=N;i++)CIN(q[i]);
return 1;
}
int getMinLeft(int lef)
{
int ans = (1<<30);
for(int i=0;i<=N;i++)
{
if(visit[i])continue;
if(ans>len[lef][i])ans=len[lef][i];
}
return ans;
}
int getMinRight(int rig)
{
int ans = (1<<30);
for(int i=0;i<=N;i++)
{
if(visit[i])continue;
if(ans>len[i][rig])ans=len[i][rig];
}
return ans;
}
int getPoint(int last,int key)//城市last后接上城市key的迫切度
{
visit[key]=1;
int ans = getMinLeft(last)+getMinRight(key)-len[last][key]*2;
visit[key]=0;
return ans;
}
int getEmerg(int qsum, int last)//last后接上哪个城市的迫切度最高
{
int themin = (1<<31),ans=0;
for(int i=1;i<=N;i++)
{
if(visit[i])continue;
if(qsum+q[i]>SIZE)continue;
if(last==i)continue;
int res=getPoint(last,i);
if(themin<res)ans=i,themin=res;
}
if(len[last][ans]>len[last][0]+len[0][ans])return 0;
return ans;
}
vector<vector<int>> calculate()
{
int qsum;//车载
int last;//上一个城市
int cityNum=N;
vector<vector<int>>ans;//答案路径
while(cityNum)
{
qsum=last=0;
vector<int>tmp;
tmp.push_back(0);
while(cityNum && qsum<SIZE)
{
int key=getEmerg(qsum,last);
if(key==0)break;
qsum+=q[key];
cityNum--;
last=key;
tmp.push_back(key);
visit[key]=1;
}
tmp.push_back(0);
ans.push_back(tmp);
}
for(int i=1;i<=N;i++)visit[i]=0;
return ans;
}
int speed(int q)
{
if(q<500)return 100;
if(q<800)return 90;
if(q<1200)return 80;
if(q<1500)return 70;
return 60;
}
int getTime(vector<int>v)//一条路径的时间
{
int s=0;
int ans=0;
for(int i=v.size()-1;i>0;i--)
ans+=(len[v[i-1]][v[i]]*60+speed(s)-1)/speed(s),s+=q[v[i-1]];
return ans;
}
int ansTime(vector<vector<int>>v)
{
int s=0;
for(int i=0;i<v.size();i++)s+=getTime(v[i]);
return s;
}
int getSum(vector<int>v)//一条路径的总货物
{
int ans=0;
for(int i=1;i<v.size()-1;i++)ans+=q[v[i]];
return ans;
}
void test()
{
int n= 7 ;
cout<<n<<endl;
for(int i=0;i<=n;i++)
{
for(int i=0;i<=n;i++)cout<<rand()%100*50<<" ";
cout<<endl;
}
cout<<endl;
for(int i=1;i<=n;i++)cout<<rand()%2000<<" ";
}
#ifdef windows
int main()
#else
int main(int argc, char *argv[])
#endif
{
std::cout << "Begin" << std::endl;
#ifdef windows
string deliveryFile = "delivery.txt";
string answerFile = "answer.txt";
#else
if (argc < MAX_ARG_NUM) {
std::cout << "please input args: delivery.txt answer.txt" << std::endl;
exit(INVALID_CODE);
}
string deliveryFile = argv[DELIVERY_ARG_INDEX];
string answerFile = argv[ANSWER_ARG_INDEX];
#endif
std::cout << deliveryFile << " " << answerFile << std::endl;
test();
FILE *pin = freopen(deliveryFile.c_str(),"r",stdin);
FILE *pout = freopen(answerFile.c_str(),"w",stdout);
while(init()){
vector<vector<int>>ans=calculate();
cout<<ansTime(ans)<<':';
for(int i=0;i<ans.size();i++)
{
cout<<getSum(ans[i])<<':';
for(int j=0;j<ans[i].size();j++)
{
cout<<ans[i][j];
if(j<ans[i].size()-1)cout<<',';
}
if(i<ans.size()-1)cout<<';';
}
cout<<endl;
}
fclose(pin);
fclose(pout);
return NORMAL_CODE;
}
3,逐步优化(2个局部优化策略)
利用随机数得到这个用例:
5
2050 3350 1700 0 3450 1200
3900 2900 3100 3200 250 2250
4050 1350 3050 4550 4750 2100
1350 1800 4550 200 100 2650
4600 4100 1050 800 900 4750
2350 1300 3550 1900 3450 600
1667 299 1035 1894 703
更好的答案是:
//16795:1035:0,3,0;1002:0,2,5,0;1667:0,1,0;1894:0,4,0
于是考虑随机交换,来优化程序。
bool exchange(vector<vector<int>>&v)//随机交换
{
int x=rand()%v.size();
int a=rand()%(v[x].size()-2)+1,b=rand()%(v[x].size()-2)+1;
if(a==b)return false;//注意判断
int oldTime=getTime(v[x]);
v[x][a]^=v[x][b]^=v[x][a]^=v[x][b];
if(getTime(v[x])>oldTime)
{
v[x][a]^=v[x][b]^=v[x][a]^=v[x][b];
return false;
}
return true;
}
除了随机交换每条路径上的2个城市,还可以随机交换不同路径上的2个城市
bool exchange2(vector<vector<int>>&v)//随机交换不同路径上的2个城市
{
int x=rand()%v.size(),y=rand()%v.size();
if(x==y)return false;
int a=rand()%(v[x].size()-2)+1,b=rand()%(v[y].size()-2)+1;
int oldTime=getTime(v[x])+getTime(v[y]);
v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];

探讨电商物流配送问题,通过模拟退火算法优化配送路径,减少总配送时间。介绍算法实现细节,包括基础代码、逐步优化策略及可视化测试。
最低0.47元/天 解锁文章
569





