货物运输算法——贪心、蒙特卡洛、模拟退火

目录

1,问题

2,基础代码

3,逐步优化(2个局部优化策略)

4,可视化测试

5,继续优化(再加2个局部优化策略)

6,4个局部优化策略的排列、组合、计时

7,多次重复随机优化全过程

8,模拟退火


1,问题

1.题目描述

某电商的某个商品在编号为0的城市生产,有编号为1~NN个城市购买了该商品,购买商品的数量分别为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

每个城市的需求数量必须全部满足,且一个城市只能接受一辆车的一次配送和经过;

车的数量足够多,由参赛选手规划车辆数量;

每辆车均不允许超载;

两两城市均联通,城市AB的距离和BA的距离可能不同;

忽略其他题目未描述的时间消耗,包括交通堵塞时间,加油时间,人工休息时间,卸货时间等均不计算。

3.题目输入

输入以文件“delivery.txt”给出。文档中将包含多个测试用例,每个用例由一组数据组成:

第一行包含一个整数N表示需要商品的城市个数,其中(1≤N≤100)。

然后是N+1行,每行含有N+1个整数,即N+1矩阵,表示N+1个城市两两之间的距离,Dij表示城市i到城市j的距离。

然后再是N行,表示每个城市需要的商品G的数量为Qi0<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];
    if(getTime(v[x])+getTime(v[y])>oldTime)
    {
        v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];
        return false;
    }
    return true;
}

加上定时函数。
exchange1和exchange2都是单点交换,完全可以合并逻辑,这里把exchange1干掉了

#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 qsum,int lef)
{
    int ans = (1<<30);
    for(int i=0;i<=N;i++)
    {
        if(visit[i])continue;
        if(qsum+q[i]>SIZE)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(q[i]+q[rig]>SIZE)continue;
        if(ans>len[i][rig])ans=len[i][rig];
    }
    return ans;
}

int getPoint(int qsum,int last,int key)//城市last后接上城市key的迫切度
{
    visit[key]=1;
    int ans = getMinLeft(qsum,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(qsum,last,i);
        if(themin<res)ans=i,themin=res;
    }
    if(len[last][ans]>len[last][0]+len[0][ans])return 0;//局部策略:(a,b)>(a,0)+(0,b)则拆
    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;
}

bool exchange2(vector<vector<int>>&v)//随机交换不同路径上的2个城市
{
    int x=rand()%v.size(),y=rand()%v.size();
    int a=rand()%(v[x].size()-2)+1,b=rand()%(v[y].size()-2)+1;
    if(v[x][a]==v[y][b])return false;
    int oldTime=getTime(v[x])+getTime(v[y]);
    v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];
    if(getTime(v[x])+getTime(v[y])>=oldTime || getSum(v[x])>SIZE || getSum(v[y])>SIZE)
    {
        v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];
        return false;
    }
    return true;
}
void change(vector<int>&v1,vector<int>&v2,int k1,int k2)//交叉交换
{
    vector<int>v3,v4;
    v3.resize(v1.size()-k1);
    copy(v1.begin()+k1,v1.end(),v3.begin());
    v4.resize(v2.size()-k2);
    copy(v2.begin()+k2,v2.end(),v4.begin());
    v1.resize(k1+v4.size());
    v2.resize(k2+v3.size());
    copy(v3.begin(),v3.end(),v2.begin()+k2);
    copy(v4.begin(),v4.end(),v1.begin()+k1);
}
bool exchange3(vector<vector<int>>&v)//随机交叉交换
{
    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]);
    change(v[x],v[y],a,b);
    if(getTime(v[x])+getTime(v[y])>=oldTime || getSum(v[x])>SIZE || getSum(v[y])>SIZE)
    {
        change(v[x],v[y],a,b);
        return false;
    }
    return true;
}

void outAns(vector<vector<int>>ans)
{
    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;
}

void test()
{
    srand(4356);
    int n= 30 ;
    cout<<n<<endl;
    for(int i=0;i<=n;i++)
    {
        for(int i=0;i<=n;i++)
        {
            cout<<rand()%100*50<<" ";
            if(i%10==0)cout<<endl;
        }
        cout<<endl;
    }
    for(int i=1;i<=n;i++)
    {
        cout<<rand()%2000<<" ";
        if(i%10==0)cout<<endl;
    }
}


#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();

    clock_t startTime,endTime;
    
    FILE *pin = freopen(deliveryFile.c_str(),"r",stdin);
    FILE *pout = freopen(answerFile.c_str(),"w",stdout);
    while(init())
    {
        startTime = clock();
        vector<vector<int>>ans=calculate();
        outAns(ans);
        while(true)
        {
            if(exchange2(ans))outAns(ans);
            if(exchange3(ans))outAns(ans);
            endTime = clock();
            if(endTime-startTime>1000*5)break;
        }
    }
    fclose(pin);
    fclose(pout);

    return NORMAL_CODE;
}

对于同样的ans,在不同随机数下开始随机优化,最后的结果是不一样的,
正如在二维曲面寻找最小值,可能会找到不同的极小值,但是不是最小值。
所以需要改进,需要重复多次从初始ans开始随机优化这个过程

首先,我们需要知道,100个城市,需要多少秒才能充分完成随机交换这个过程
根据下面的运行结果,我本地是12秒。
然而,在linux机器上,这个时间连1秒都不到,刚开始我以为是性能的差距,后来才发现,linux的随机数质量比windows高太多了。
所以linux跑出来的答案也比winodws要好。


vector<vector<int>> calculate2()
{
    clock_t startTime,endTime;
    startTime = clock();
    vector<vector<int>>ans=calculate();
    outAns(ans);
    while(true)
    {
        if(exchange1(ans))
        {
            //outAns(ans);
            endTime = clock();
            cout<<endTime-startTime<<endl;
        }
        if(exchange2(ans))
        {
            //outAns(ans);
            endTime = clock();
            cout<<endTime-startTime<<endl;
        }
        endTime = clock();
        if(endTime-startTime>theTime*1000*200)break;
    }
    cout<<endl;
    return ans;
}

#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,tmp;
        ans=calculate2();
        for(int i=0;i<20;i++)
        {
            tmp=calculate2();
            if(ansTime(ans)>ansTime(tmp))ans=tmp;
        }
        outAns(ans);
    }
    fclose(pin);
    fclose(pout);

    return NORMAL_CODE;
}

windows跑10次:
182944  181182   175900    177399   181807     177817     180889   183269    177706    180825
最好的结果175900
linux跑10次:
176143   181143    173314    173268   180938     179138    176469   179536    168285
最好的结果168285

4,可视化测试

除了随机产生2个城市的距离的用例,我觉得还需要专门测一测二维空间的例子。

我又写了个先产生随机坐标再计算两两距离的代码:

#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();}
#ifdef windows
    #define theTime 1
#else
    #define theTime 1000
#endif

#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 qsum,int lef)
{
    int ans = (1<<30);
    for(int i=0;i<=N;i++)
    {
        if(visit[i])continue;
        if(qsum+q[i]>SIZE)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(q[i]+q[rig]>SIZE)continue;
        if(ans>len[i][rig])ans=len[i][rig];
    }
    return ans;
}

int getPoint(int qsum,int last,int key)//城市last后接上城市key的迫切度
{
    visit[key]=1;
    int ans = getMinLeft(qsum,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(qsum,last,i);
        if(themin<res)ans=i,themin=res;
    }
    if(len[last][ans]>len[last][0]+len[0][ans])return 0;//局部策略:(a,b)>(a,0)+(0,b)则拆
    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;
}

bool exchange1(vector<vector<int>>&v)//随机交换不同路径上的2个城市
{
    int x=rand()%v.size(),y=rand()%v.size();
    int a=rand()%(v[x].size()-2)+1,b=rand()%(v[y].size()-2)+1;
    if(v[x][a]==v[y][b])return false;
    int oldTime=getTime(v[x])+getTime(v[y]);
    v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];
    if(getTime(v[x])+getTime(v[y])>=oldTime || getSum(v[x])>SIZE || getSum(v[y])>SIZE)
    {
        v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];
        return false;
    }
    return true;
}

//交叉交换,v1分成前面k1个和后面的v1.size()-k1个,v2同理,交换后面的2段
template<typename T>
void change(vector<T>&v1,vector<T>&v2,int k1,int k2)
{
    vector<T>v3,v4;
    v3.resize(v1.size()-k1);
    copy(v1.begin()+k1,v1.end(),v3.begin());
    v4.resize(v2.size()-k2);
    copy(v2.begin()+k2,v2.end(),v4.begin());
    v1.resize(k1+v4.size());
    v2.resize(k2+v3.size());
    copy(v3.begin(),v3.end(),v2.begin()+k2);
    copy(v4.begin(),v4.end(),v1.begin()+k1);
}
bool exchange2(vector<vector<int>>&v)//随机交叉交换
{
    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]);
    change(v[x],v[y],a,b);
    if(getTime(v[x])+getTime(v[y])>=oldTime || getSum(v[x])>SIZE || getSum(v[y])>SIZE)
    {
        change(v[x],v[y],a,b);
        return false;
    }
    return true;
}

void outAns(vector<vector<int>>ans)
{
    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;
}

vector<vector<int>> calculate2()
{
    clock_t startTime,endTime;
    startTime = clock();
    vector<vector<int>>ans=calculate();
    outAns(ans);
    while(true)
    {
        if(exchange1(ans))
        {
            outAns(ans);
            endTime = clock();
            cout<<endTime-startTime<<endl;
        }
        if(exchange2(ans))
        {
            outAns(ans);
            endTime = clock();
            cout<<endTime-startTime<<endl;
        }
        endTime = clock();
        if(endTime-startTime>theTime*1000)break;
    }
    cout<<endl;
    return ans;
}

void test()
{
    srand(4356);
    int n= 60 ;
    cout<<n<<endl;
    for(int i=0;i<=n;i++)
    {
        for(int i=1;i<=n+1;i++)
        {
            cout<<rand()%5000<<" ";
            if(i%10==0)cout<<endl;
        }
        cout<<endl;
    }
    for(int i=1;i<=n;i++)
    {
        cout<<rand()%2000<<" ";
        if(i%10==0)cout<<endl;
    }
}

void test2()
{
    srand(4367865);
    int n= 6 ;
    int x[101],y[101];
    cout<<n<<endl;
    for(int i=0;i<=n;i++)
    {
        x[i]=rand()%3000,y[i]=rand()%3000;
    }
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=n;j++)
        {
            cout<<(int)sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]))<<" ";
            if(j && j%10==0)cout<<endl;
        }
        cout<<endl;
    }
    for(int i=1;i<=n;i++)
    {
        cout<<rand()%2000<<" ";
        if(i%10==0)cout<<endl;
    }
    cout<<endl;
    for(int i=0;i<=n;i++)cout<<x[i]<<' '<<y[i]<<endl;
}


#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;
    test2();
    FILE *pin = freopen(deliveryFile.c_str(),"r",stdin);
    FILE *pout = freopen(answerFile.c_str(),"w",stdout);
    while(init())
    {
        vector<vector<int>>ans,tmp;
        ans=calculate2();
        for(int i=0;i<10;i++)
        {
            tmp=calculate2();
            if(ansTime(ans)>ansTime(tmp))ans=tmp;
        }
        outAns(ans);
    }
    fclose(pin);
    fclose(pout);

    return NORMAL_CODE;
}

控制台输出:

6
0 3130 2337 612 2409 1390 1827
3130 0 805 2631 2102 1838 1963
2337 805 0 1825 1591 1035 1272
612 2631 1825 0 1810 817 1217
2409 2102 1591 1810 0 1306 627
1390 1838 1035 817 1306 0 693
1827 1963 1272 1217 627 693 0
1801 1568 1224 180 312 521
567 216
2045 2976
1777 2216
1098 521
2836 1028
1542 1208
2209 1019

然后用excel把这些点可视化出来:

对照答案:

12734:1880:0,2,5,0;701:0,6,4,0;1801:0,1,0;1224:0,3,0

看下哪些数据和聚类偏离较大,我们发现点1没有和点2放在一起,再看看1的需求量1801,所以这个用例通过。

5,继续优化(再加2个局部优化策略)

新增2个局部优化策略:

随机拆分路径、随机拼装路径

//随机拆分
bool split(vector<vector<int>>&v,double p)
{
    int x=rand()%v.size();
    int a=rand()%(v[x].size()-2)+1;
    if(a==1)return false;
    int oldTime=getTime(v[x]);
    vector<int>tmp=v[x];
    vector<int>vc=fchai(v[x],a);
    vc.insert(vc.begin(),0);
    v[x].push_back(0);
    if(getTime(v[x])+getTime(vc)>oldTime*(1+rand()%100*p/100)-0.1 )
    {
        v[x]=tmp;
        return false;
    }
    v.push_back(vc);
    return true;
}
//随机组合
bool merge(vector<vector<int>>&v,double p)
{
    int x=rand()%v.size(),y=rand()%v.size();
    if(x==y)return false;
    vector<int>vm=join(v[x],v[y]);
    vm.erase(vm.begin()+v[x].size()-1);
    vm.erase(vm.begin()+v[x].size()-1);
    int oldTime=getTime(v[x])+getTime(v[y]);
    if(getTime(vm)>oldTime*(1+rand()%100*p/100)-0.1 || getSum(vm)>SIZE)
    {
        return false;
    }
    if(x>y)x^=y^=x^=y;
    v.erase(v.begin()+y);
    v.erase(v.begin()+x);
    v.push_back(vm);
    return true;
}

6,4个局部优化策略的排列、组合、计时

4个策略,应该按照AAAAAA BBBBBB CCCCCC DDDDDD的形式来调用,还是按照ABCD ABCD ABCD ABCD ABCD ABCD的形式呢?

我感觉应该是后面这个好一点,但是merge策略比较特殊,所以采用ABCABCABCDDD ABCABCABCABCDDD的形式好像更好。

那么,每一段ABCABCABC和每一段DDD需要多久呢

经过实测,对于100个城市来说,分别给1.5秒和0.5秒是相当充分的。

那么,整个大循环需要重复多少次呢?

经过实测,对于100个城市来说,重复3次是相当充分的。

vector<vector<int>> calculate2() 
{
    vector<vector<int>>ans=calculate();
    //outAns(ans);
    for(int i=0;i<3;i++)
    {
        clock_t startTime,endTime;
        startTime = clock();
        while(true)
        {            
            if(exchange1(ans,0))
            {
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            if(exchange2(ans,0))
            {
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            if(split(ans,0))
            {
                //cout<<"split  ";
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            endTime = clock();
            if(endTime-startTime>theTime*1500)break;  //1.5秒
        }
        startTime = clock();
        while(true)
        {
            if(merge(ans,0))
            {
                //cout<<"merge  ";
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            endTime = clock();
            if(endTime-startTime>theTime*300)break;  //0.5秒
        }
    }
    cout<<endl;
    cout<<ansTime(ans)<<"   ";
    return ans;
}

7,多次重复随机优化全过程

不同的随机数,对于随机优化的结果影响很大。

所以需要多次重复从初始值开始优化的全过程。

需要重复多少次呢?我先重复100次试试看:

这个结果让我感觉,100次就刚刚好。

100次需要10分钟,整个用例给的是40分钟,后面还要加模拟退火,所以10分钟很合适。

重新试下,跑200次,看下结果:

最好的结果是第149次,其次是第67次,两个差距非常非常小,所以100次应该是OK了。

8,模拟退火

前面所有的想法都是我一点点想出来的,其实模拟退火的思想已经包含在其中了。

但是实现细节还是有差别,所以我再按照模拟退火来算一下,看能不能得到更优解。

#define windows

#include<iostream>
#include<string>
#include<vector>
#include<time.h>
#include<functional>
#include<algorithm>
#include<map>
#include "csimsgeek.h"
using namespace std;


#define CIN(x) while(!(cin>>x)){cin.clear();cin.ignore();}
#ifdef windows
    #define theTime 1
#else
    #define theTime 1000
#endif

#define SIZE 2000
int N;//城市0,1—N
vector<vector<int>>len;//两两之间的距离
int q[101];//每个城市的需求量
int visit[101];//访问标记
vector<vector<int>>jxans;
map<vector<vector<int>>,int>m;

//读取输入,初始化
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;
}
//lef到任意城市的最小距离
int getMinLeft(int qsum,int lef)
{
    int ans = (1<<30);
    for(int i=0;i<=N;i++)
    {
        if(visit[i])continue;
        if(qsum+q[i]>SIZE)continue;
        if(ans>len[lef][i])ans=len[lef][i];
    }
    return ans;
}
//任意城市到rig的最小距离
int getMinRight(int rig)
{
    int ans = (1<<30);
    for(int i=0;i<=N;i++)
    {
        if(visit[i])continue;
        if(q[i]+q[rig]>SIZE)continue;
        if(ans>len[i][rig])ans=len[i][rig];
    }
    return ans;
}
//城市last后接上城市key的迫切度
int getPoint(int qsum,int last,int key)
{
    visit[key]=1;
    int ans = getMinLeft(qsum,last)+getMinRight(key)-len[last][key]*2;
    visit[key]=0;
    return ans;
}
//last后接上哪个城市的迫切度最高
int getEmerg(int qsum, int 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(qsum,last,i);
        if(themin<res)ans=i,themin=res;
    }
    if(len[last][ans]>len[last][0]+len[0][ans])return 0;//局部策略:(a,b)>(a,0)+(0,b)则拆
    return ans;
}

//获得一个可行解
vector<vector<int>> calculate()
{
#ifdef TEST
    return jxans;
#endif
    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;
}
//随机交换不同路径上的2个城市,p=0或1
bool exchange1(vector<vector<int>>&v,double p)
{
    int x=rand()%v.size(),y=rand()%v.size();
    int a=rand()%(v[x].size()-2)+1,b=rand()%(v[y].size()-2)+1;
    if(v[x][a]==v[y][b])return false;
    int oldTime=getTime(v[x])+getTime(v[y]);
    v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];
    if(getTime(v[x])+getTime(v[y])>=oldTime*(1+rand()%100*p/100)-0.1 || getSum(v[x])>SIZE || getSum(v[y])>SIZE)
    {
        v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];
        return false;
    }
    return true;
}
//把v分成前面k个和后面的v.size()-k个,返回拆出来的后面的数组,v本身只留k个
template<typename T>
vector<T> fchai(vector<T>&v,int k)
{
    vector<T>v2;
    v2.resize(v.size()-k);
    copy(v.begin()+k,v.end(),v2.begin());
    v.resize(k);
    return v2;
}
//2个vector拼接起来
template<typename T>
vector<T> join(vector<T>&v1,vector<T>&v2)
{
    vector<T>ans(v1.size()+v2.size());
    copy(v1.begin(),v1.end(),ans.begin());
    copy(v2.begin(),v2.end(),ans.begin()+v1.size());
    return ans;
}
//交叉交换,v1分成前面k1个和后面的v1.size()-k1个,v2同理,交换后面的2段
template<typename T>
void change(vector<T>&v1,vector<T>&v2,int k1,int k2)
{
    vector<T>v3=fchai(v1,k1),v4=fchai(v2,k2);
    v1=join(v1,v4),v2=join(v2,v3);
}
//随机交叉交换
bool exchange2(vector<vector<int>>&v,double p)
{
    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]);
    change(v[x],v[y],a,b);
    if(getTime(v[x])+getTime(v[y])>=oldTime*(1+rand()%100*p/100)-0.1 || getSum(v[x])>SIZE || getSum(v[y])>SIZE)
    {
        change(v[x],v[y],a,b);
        return false;
    }
    return true;
}
//随机拆分
bool split(vector<vector<int>>&v,double p)
{
    int x=rand()%v.size();
    int a=rand()%(v[x].size()-2)+1;
    if(a==1)return false;
    int oldTime=getTime(v[x]);
    vector<int>tmp=v[x];
    vector<int>vc=fchai(v[x],a);
    vc.insert(vc.begin(),0);
    v[x].push_back(0);
    if(getTime(v[x])+getTime(vc)>oldTime*(1+rand()%100*p/100)-0.1 )
    {
        v[x]=tmp;
        return false;
    }
    v.push_back(vc);
    return true;
}
//随机组合
bool merge(vector<vector<int>>&v,double p)
{
    int x=rand()%v.size(),y=rand()%v.size();
    if(x==y)return false;
    vector<int>vm=join(v[x],v[y]);
    vm.erase(vm.begin()+v[x].size()-1);
    vm.erase(vm.begin()+v[x].size()-1);
    int oldTime=getTime(v[x])+getTime(v[y]);
    if(getTime(vm)>oldTime*(1+rand()%100*p/100)-0.1 || getSum(vm)>SIZE)
    {
        return false;
    }
    if(x>y)x^=y^=x^=y;
    v.erase(v.begin()+y);
    v.erase(v.begin()+x);
    v.push_back(vm);
    return true;
}

//输出方案
void outAns(vector<vector<int>>ans)
{
    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;
}

//一轮随机优化,需要6秒
void calculate2(vector<vector<int>>&lastAns) 
{
    cout<<"calculate2 开始\n";
    vector<vector<int>>ans=calculate();
    //outAns(ans);
    for(int i=0;i<3;i++)
    {
        clock_t startTime,endTime;
        startTime = clock();
        while(true)
        {            
            if(exchange1(ans,0))
            {
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            if(exchange2(ans,0))
            {
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            if(split(ans,0))
            {
                //cout<<"split  ";
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            endTime = clock();
            if(endTime-startTime>theTime*1500)break;  //1.5秒
        }
        startTime = clock();
        while(true)
        {
            if(merge(ans,0))
            {
                //cout<<"merge  ";
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            endTime = clock();
            if(endTime-startTime>theTime*300)break;  //0.5秒
        }
    }
    cout<<endl;
    cout<<ansTime(ans)<<"   ";
    if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
    cout<<"calculate2 结束\n";
}
//模拟退火
void calculate3(vector<vector<int>>&lastAns) 
{
    cout<<"calculate3\n";
    vector<vector<int>>ans=calculate();
    //outAns(ans);
    for(int i=0;i<3;i++)
    {
        clock_t startTime,endTime;
        startTime = clock();
        while(true)
        {            
            if(exchange1(ans,0))
            {
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            if(exchange2(ans,1))
            {
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            if(split(ans,0))
            {
                //cout<<"split  ";
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            endTime = clock();
            if(endTime-startTime>theTime*1500)break;  //1.5秒
        }
        startTime = clock();
        while(true)
        {
            if(merge(ans,1))
            {
                //cout<<"merge  ";
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            endTime = clock();
            if(endTime-startTime>theTime*300)break;  //0.5秒
        }
    }
    cout<<endl;
    cout<<ansTime(ans)<<"   ";
    calculate2(ans);
    if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
}

#ifdef windows
void test()
{
    srand(4356);
    int n= 60 ;
    cout<<n<<endl;
    for(int i=0;i<=n;i++)
    {
        for(int i=1;i<=n+1;i++)
        {
            cout<<rand()%5000<<" ";
            if(i%10==0)cout<<endl;
        }
        cout<<endl;
    }
    for(int i=1;i<=n;i++)
    {
        cout<<rand()%500<<" ";
        if(i%10==0)cout<<endl;
    }
}
void test2()
{
    srand(4367865);
    int n= 50 ;
    int x[101],y[101];
    cout<<n<<endl;
    for(int i=0;i<=n;i++)
    {
        x[i]=rand()%3000,y[i]=rand()%3000;
    }
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=n;j++)
        {
            cout<<(int)sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]))<<" ";
            if(j && j%10==0)cout<<endl;
        }
        cout<<endl;
    }
    for(int i=1;i<=n;i++)
    {
        cout<<rand()%2000<<" ";
        if(i%10==0)cout<<endl;
    }
    cout<<endl;
    for(int i=0;i<=n;i++)cout<<x[i]<<' '<<y[i]<<endl;
}
void test3()
{
    for(int i=1;i<=50;i++)cout<<"0 "<<i<<" 0 -1 ";
    cout<<endl;
}
#endif

#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";
    //test3();
#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;
    FILE *pin = freopen(deliveryFile.c_str(),"r",stdin);
    FILE *pout = freopen(answerFile.c_str(),"w",stdout);
    while(init())
    {
#ifdef TEST
        int lujing =50; //需要硬编码
        jxans.resize(lujing);
        for(int i=0;i<lujing;i++)jxans[i].resize(101);
        for(int i=0;i<lujing;i++)
        {
            for(int j=0;j<101;j++)
            {
                CIN(jxans[i][j]);
                if(jxans[i][j]==-1)
                {
                    jxans[i].resize(j);
                    break;
                }
            }
        }
#endif
        vector<vector<int>>ans,tmp;
        ans=calculate();
        outAns(ans);
        for(int i=0;i<100;i++)
        {
            calculate2(ans);
            outAns(ans);
        }
        outAns(ans);
        for(int i=0;i<100;i++)
        {
            calculate3(ans);
            outAns(ans);
        }
    }
    fclose(pin);
    fclose(pout);
    return NORMAL_CODE;
}

结果发现模拟退火并没有什么效果。

我想到之前调测的时候,即使初始可行解是通解:0 1 0,0 2 0,......,0 N 0,随机交换之后的结果也是一样好的,所以,把模拟退火的初始解换成这个通解或许有效?

//模拟退火
void calculate4(vector<vector<int>>&lastAns) 
{
    cout<<"calculate4\n";
    vector<vector<int>>ans(N);
    vector<int>tmp(3);
    tmp[0]=tmp[2]=0;
    for(int i=0;i<N;i++)
    {
        tmp[1]=i+1;
        ans[i]=tmp;
    }
    //outAns(ans);
    for(int i=0;i<3;i++)
    {
        clock_t startTime,endTime;
        startTime = clock();
        while(true)
        {            
            if(exchange1(ans,1))
            {
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            if(exchange2(ans,1))
            {
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            if(split(ans,1))
            {
                //cout<<"split  ";
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            endTime = clock();
            if(endTime-startTime>theTime*1500)break;  //1.5秒
        }
        startTime = clock();
        while(true)
        {
            if(merge(ans,1))
            {
                //cout<<"merge  ";
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            endTime = clock();
            if(endTime-startTime>theTime*300)break;  //0.5秒
        }
    }
    cout<<endl;
    cout<<ansTime(ans)<<"   ";
    calculate2(ans);//6秒
    if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
}

然而,这个写法有个很大的问题!

calculate4里面,最后调用calculate2,但是calculate2里面的初始可行解还是calculate得到的,而不是calculate4得到的。

对算法我们一定要保持敬畏,任何细微的差别都可能造成算法完全不一样

正确做法:

calculate2接受1个参数,以这个参数作为初始可行解开始随机优化。

#define windows
//#define TEST

#include<iostream>
#include<string>
#include<vector>
#include<time.h>
#include<functional>
#include<algorithm>
#include<map>
#include "csimsgeek.h"
using namespace std;


#define CIN(x) while(!(cin>>x)){cin.clear();cin.ignore();}
#ifdef windows
    #define theTime 1
#else
    #define theTime 1000
#endif

#define SIZE 2000
int N;//城市0,1—N
vector<vector<int>>len;//两两之间的距离
int q[101];//每个城市的需求量
int visit[101];//访问标记
vector<vector<int>>jxans;
map<vector<vector<int>>,int>m;

//读取输入,初始化
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;
}
//lef到任意城市的最小距离
int getMinLeft(int qsum,int lef)
{
    int ans = (1<<30);
    for(int i=0;i<=N;i++)
    {
        if(visit[i])continue;
        if(qsum+q[i]>SIZE)continue;
        if(ans>len[lef][i])ans=len[lef][i];
    }
    return ans;
}
//任意城市到rig的最小距离
int getMinRight(int rig)
{
    int ans = (1<<30);
    for(int i=0;i<=N;i++)
    {
        if(visit[i])continue;
        if(q[i]+q[rig]>SIZE)continue;
        if(ans>len[i][rig])ans=len[i][rig];
    }
    return ans;
}
//城市last后接上城市key的迫切度
int getPoint(int qsum,int last,int key)
{
    visit[key]=1;
    int ans = getMinLeft(qsum,last)+getMinRight(key)-len[last][key]*2;
    visit[key]=0;
    return ans;
}
//last后接上哪个城市的迫切度最高
int getEmerg(int qsum, int 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(qsum,last,i);
        if(themin<res)ans=i,themin=res;
    }
    if(len[last][ans]>len[last][0]+len[0][ans])return 0;//局部策略:(a,b)>(a,0)+(0,b)则拆
    return ans;
}

//获得一个可行解
vector<vector<int>> calculate()
{
#ifdef TEST
    return jxans;
#endif
    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;
}
//随机交换不同路径上的2个城市,p=0或1
bool exchange1(vector<vector<int>>&v,double p)
{
    int x=rand()%v.size(),y=rand()%v.size();
    int a=rand()%(v[x].size()-2)+1,b=rand()%(v[y].size()-2)+1;
    if(v[x][a]==v[y][b])return false;
    int oldTime=getTime(v[x])+getTime(v[y]);
    v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];
    if(getTime(v[x])+getTime(v[y])>=oldTime*(1+rand()%100*p/100)-0.1 || getSum(v[x])>SIZE || getSum(v[y])>SIZE)
    {
        v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];
        return false;
    }
    return true;
}
//把v分成前面k个和后面的v.size()-k个,返回拆出来的后面的数组,v本身只留k个
template<typename T>
vector<T> fchai(vector<T>&v,int k)
{
    vector<T>v2;
    v2.resize(v.size()-k);
    copy(v.begin()+k,v.end(),v2.begin());
    v.resize(k);
    return v2;
}
//2个vector拼接起来
template<typename T>
vector<T> join(vector<T>&v1,vector<T>&v2)
{
    vector<T>ans(v1.size()+v2.size());
    copy(v1.begin(),v1.end(),ans.begin());
    copy(v2.begin(),v2.end(),ans.begin()+v1.size());
    return ans;
}
//交叉交换,v1分成前面k1个和后面的v1.size()-k1个,v2同理,交换后面的2段
template<typename T>
void change(vector<T>&v1,vector<T>&v2,int k1,int k2)
{
    vector<T>v3=fchai(v1,k1),v4=fchai(v2,k2);
    v1=join(v1,v4),v2=join(v2,v3);
}
//随机交叉交换
bool exchange2(vector<vector<int>>&v,double p)
{
    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]);
    change(v[x],v[y],a,b);
    if(getTime(v[x])+getTime(v[y])>=oldTime*(1+rand()%100*p/100)-0.1 || getSum(v[x])>SIZE || getSum(v[y])>SIZE)
    {
        change(v[x],v[y],a,b);
        return false;
    }
    return true;
}
//随机拆分
bool split(vector<vector<int>>&v,double p)
{
    int x=rand()%v.size();
    int a=rand()%(v[x].size()-2)+1;
    if(a==1)return false;
    int oldTime=getTime(v[x]);
    vector<int>tmp=v[x];
    vector<int>vc=fchai(v[x],a);
    vc.insert(vc.begin(),0);
    v[x].push_back(0);
    if(getTime(v[x])+getTime(vc)>oldTime*(1+rand()%100*p/100)-0.1 )
    {
        v[x]=tmp;
        return false;
    }
    v.push_back(vc);
    return true;
}
//随机组合
bool merge(vector<vector<int>>&v,double p)
{
    int x=rand()%v.size(),y=rand()%v.size();
    if(x==y)return false;
    vector<int>vm=join(v[x],v[y]);
    vm.erase(vm.begin()+v[x].size()-1);
    vm.erase(vm.begin()+v[x].size()-1);
    int oldTime=getTime(v[x])+getTime(v[y]);
    if(getTime(vm)>oldTime*(1+rand()%100*p/100)-0.1 || getSum(vm)>SIZE)
    {
        return false;
    }
    if(x>y)x^=y^=x^=y;
    v.erase(v.begin()+y);
    v.erase(v.begin()+x);
    v.push_back(vm);
    return true;
}

//输出方案
void outAns(vector<vector<int>>ans)
{
    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<<")\n";
}

//一轮随机优化,需要6秒
vector<vector<int>> calculate2(vector<vector<int>>startAns) 
{
    cout<<"calculate2 开始\n";
    vector<vector<int>>ans=startAns;
    //outAns(ans);
    for(int i=0;i<3;i++)
    {
        clock_t startTime,endTime;
        startTime = clock();
        while(true)
        {            
            if(exchange1(ans,0))
            {
                //cout<<ansTime(ans)<<"   ";
                //endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            if(exchange2(ans,0))
            {
                //cout<<ansTime(ans)<<"   ";
                //endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            if(split(ans,0))
            {
                //cout<<"split  ";
                //cout<<ansTime(ans)<<"   ";
                //endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            endTime = clock();
            if(endTime-startTime>theTime*1500)break;  //1.5秒
        }
        startTime = clock();
        while(true)
        {
            if(merge(ans,0))
            {
                //cout<<"merge  ";
                //cout<<ansTime(ans)<<"   ";
                //endTime = clock();
                //cout<<endTime-startTime<<endl;
            }
            endTime = clock();
            if(endTime-startTime>theTime*500)break;  //0.5秒
        }
    }
    //cout<<endl;
    cout<<ansTime(ans)<<"   ";
    cout<<"calculate2 结束\n";
    return ans;
}
//模拟退火,需要12秒
void calculate3(vector<vector<int>>&lastAns) 
{
    //cout<<"calculate3\n";
    vector<vector<int>>ans=calculate();
    //outAns(ans);
    for(int i=0;i<3;i++)
    {
        clock_t startTime,endTime;
        startTime = clock();
        while(true)
        {            
            if(exchange1(ans,1))
            {
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            if(exchange2(ans,1))
            {
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            if(split(ans,1))
            {
                //cout<<"split  ";
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            endTime = clock();
            if(endTime-startTime>theTime*1500)break;  //1.5秒
        }
        startTime = clock();
        while(true)
        {
            if(merge(ans,1))
            {
                //cout<<"merge  ";
                //cout<<ansTime(ans)<<"   ";
                endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            endTime = clock();
            if(endTime-startTime>theTime*500)break;  //0.5秒
        }
    }
    cout<<endl;
    cout<<ansTime(ans)<<"   ";
    calculate2(ans);//6秒
    if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
}
//模拟退火,需要12秒
void calculate4(vector<vector<int>>&lastAns) 
{
    cout<<"calculate4\n";
    vector<vector<int>>ans(N);
    vector<int>tmp(3);
    tmp[0]=tmp[2]=0;
    for(int i=0;i<N;i++)
    {
        tmp[1]=i+1;
        ans[i]=tmp;
    }
    //outAns(ans);
    for(int i=0;i<3;i++)
    {
        clock_t startTime,endTime;
        startTime = clock();
        while(true)
        {            
            if(exchange1(ans,1))
            {
                //cout<<ansTime(ans)<<"   ";
                //endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            if(exchange2(ans,1))
            {
                //cout<<ansTime(ans)<<"   ";
                //endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            if(split(ans,1))
            {
                //cout<<"split  ";
                //cout<<ansTime(ans)<<"   ";
                //endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            endTime = clock();
            if(endTime-startTime>theTime*1500)break;  //1.5秒
        }
        startTime = clock();
        while(true)
        {
            if(merge(ans,1))
            {
                //cout<<"merge  ";
                //cout<<ansTime(ans)<<"   ";
                //endTime = clock();
                //cout<<endTime-startTime<<endl;
                if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
            }
            endTime = clock();
            if(endTime-startTime>theTime*500)break;  //0.5秒
        }
    }
    ans = calculate2(ans);//6秒
    cout<<ansTime(ans)<<"   ";
    if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
}
#ifdef windows
void test()
{
    srand(4356);
    int n= 60 ;
    cout<<n<<endl;
    for(int i=0;i<=n;i++)
    {
        for(int i=1;i<=n+1;i++)
        {
            cout<<rand()%5000<<" ";
            if(i%10==0)cout<<endl;
        }
        cout<<endl;
    }
    for(int i=1;i<=n;i++)
    {
        cout<<rand()%500<<" ";
        if(i%10==0)cout<<endl;
    }
}
void test2()
{
    srand(3346);
    int n= 100 ;
    int x[101],y[101];
    cout<<n<<endl;
    for(int i=0;i<=n;i++)
    {
        x[i]=rand()%3000,y[i]=rand()%3000;
    }
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=n;j++)
        {
            if(j)cout<<',';
            cout<<(int)sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
            //if(j && j%10==0)cout<<endl;
        }
        cout<<endl;
    }
    for(int i=1;i<=n;i++)
    {
        cout<<rand()%500<<endl;
    }
    //cout<<endl;
    for(int i=0;i<=n;i++)cout<<x[i]<<' '<<y[i]<<endl;
}
void test3()
{
    for(int i=1;i<=50;i++)cout<<"0 "<<i<<" 0 -1 ";
    cout<<endl;
}
#endif

#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;
    FILE *pin = freopen(deliveryFile.c_str(),"r",stdin);
    FILE *pout = freopen(answerFile.c_str(),"w",stdout);
    while(init())
    {
#ifdef TEST
        int lujing =50; //需要硬编码
        jxans.resize(lujing);
        for(int i=0;i<lujing;i++)jxans[i].resize(101);
        for(int i=0;i<lujing;i++)
        {
            for(int j=0;j<101;j++)
            {
                CIN(jxans[i][j]);
                if(jxans[i][j]==-1)
                {
                    jxans[i].resize(j);
                    break;
                }
            }
        }
#endif
        //test2();
        vector<vector<int>>ans,tmp;
        ans=calculate();
        //outAns(ans);
        for(int i=0;i<10;i++)
        {
            tmp = calculate2(calculate());
            if(ansTime(ans)>ansTime(tmp))ans=tmp;
            //outAns(ans);
        }        
        for(int i=0;i<10;i++)
        {
            calculate4(ans);
            //outAns(ans);
        }
        outAns(ans);
    }
    fclose(pin);
    fclose(pout);
    std::cout << "End" << std::endl;
    return NORMAL_CODE;
}

至此,才真正得到模拟退火算法,实测发现非常有效。

emmmmmm......还是打脸了,这个还不算是真正的模拟退火,接受概率这块写的不对。

ans1 变成 ans 2(ans2>ans1)的接受概率应该是exp((ans1-ans2)/ans1)

最终代码:



#include<iostream>
#include<string>
#include<vector>
#include<time.h>
#include<functional>
#include<algorithm>
#include<map>
#include "csimsgeek.h"
using namespace std;


#define CIN(x) while(!(cin>>x)){cin.clear();cin.ignore();}
#ifdef windows
    #define theTime 1
#else
    #define theTime 1000
#endif

#define SIZE 2000
int N;//城市0,1—N
vector<vector<int>>len;//两两之间的距离
int q[101];//每个城市的需求量
int visit[101];//访问标记
vector<vector<int>>jxans;
map<vector<vector<int>>,int>m;
int calculateFlag;
int calculateFlag4;

//读取输入,初始化
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]);
    calculateFlag=calculateFlag4=0;
    return 1;
}
//lef到任意城市的最小距离
int getMinLeft(int qsum,int lef)
{
    int ans = (1<<30);
    for(int i=0;i<=N;i++)
    {
        if(visit[i])continue;
        if(qsum+q[i]>SIZE)continue;
        if(ans>len[lef][i])ans=len[lef][i];
    }
    return ans;
}
//任意城市到rig的最小距离
int getMinRight(int rig)
{
    int ans = (1<<30);
    for(int i=0;i<=N;i++)
    {
        if(visit[i])continue;
        if(q[i]+q[rig]>SIZE)continue;
        if(ans>len[i][rig])ans=len[i][rig];
    }
    return ans;
}
//城市last后接上城市key的迫切度
int getPoint(int qsum,int last,int key)
{
    visit[key]=1;
    int ans = getMinLeft(qsum,last)+getMinRight(key)-len[last][key]*2;
    visit[key]=0;
    return ans;
}
//last后接上哪个城市的迫切度最高
int getEmerg(int qsum, int 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(qsum,last,i);
        if(themin<res)ans=i,themin=res;
    }
    if(len[last][ans]>len[last][0]+len[0][ans])return 0;//局部策略:(a,b)>(a,0)+(0,b)则拆
    return ans;
}


//获得一个可行解
vector<vector<int>> calculate()
{
#ifdef TEST
    return jxans;
#endif
    static vector<vector<int>>ans;//答案路径
    if(calculateFlag)return ans;  //让本函数只运行一次
    ans.resize(0);
    int qsum;//车载
    int last;//上一个城市
    int cityNum=N;
    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;
    calculateFlag=1;
    return ans;
}
//通用可行初始解
vector<vector<int>> calculate4() 
{
    static vector<vector<int>>ans;
    if(calculateFlag4)return ans;  //让本函数只运行一次
    ans.resize(N);
    vector<int>tmp(3);
    tmp[0]=tmp[2]=0;
    for(int i=0;i<N;i++)
    {
        tmp[1]=i+1;
        ans[i]=tmp;
    }
    calculateFlag4=1;
    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;
}

//输入ans的增量比,输出以概率性不接受的结果
bool expRand(double x)
{
    return exp(-x/3)<rand()%10000*1.0/10000;
}

//随机交换2个城市,p=0或1
bool exchange1(vector<vector<int>>&v,int flag)
{
    int x=rand()%v.size(),y=rand()%v.size();
    int a=rand()%(v[x].size()-2)+1,b=rand()%(v[y].size()-2)+1;
    if(v[x][a]==v[y][b])return false;
    int oldTime=getTime(v[x])+getTime(v[y]);
    v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];
    if(flag==0 && getTime(v[x])+getTime(v[y])>=oldTime || flag==1 && expRand((getTime(v[x])+getTime(v[y]))*1.0/oldTime) || getSum(v[x])>SIZE || getSum(v[y])>SIZE)
    {
        v[x][a]^=v[y][b]^=v[x][a]^=v[y][b];
        return false;
    }
    return getTime(v[x])+getTime(v[y])<oldTime;
}
//把v分成前面k个和后面的v.size()-k个,返回拆出来的后面的数组,v本身只留k个
template<typename T>
vector<T> fchai(vector<T>&v,int k)
{
    vector<T>v2;
    v2.resize(v.size()-k);
    copy(v.begin()+k,v.end(),v2.begin());
    v.resize(k);
    return v2;
}
//2个vector拼接起来
template<typename T>
vector<T> join(vector<T>&v1,vector<T>&v2)
{
    vector<T>ans(v1.size()+v2.size());
    copy(v1.begin(),v1.end(),ans.begin());
    copy(v2.begin(),v2.end(),ans.begin()+v1.size());
    return ans;
}
//交叉交换,v1分成前面k1个和后面的v1.size()-k1个,v2同理,交换后面的2段
template<typename T>
void change(vector<T>&v1,vector<T>&v2,int k1,int k2)
{
    vector<T>v3=fchai(v1,k1),v4=fchai(v2,k2);
    v1=join(v1,v4),v2=join(v2,v3);
}
//随机交叉交换
bool exchange2(vector<vector<int>>&v,int flag)
{
    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]);
    change(v[x],v[y],a,b);
    if(flag==0 && getTime(v[x])+getTime(v[y])>=oldTime || flag==1 && expRand((getTime(v[x])+getTime(v[y]))*1.0/oldTime) || getSum(v[x])>SIZE || getSum(v[y])>SIZE)
    {
        change(v[x],v[y],a,b);
        return false;
    }
    return getTime(v[x])+getTime(v[y])<oldTime;
}
//随机拆分
bool split(vector<vector<int>>&v,int flag)
{
    
    int x=rand()%v.size();
    int a=rand()%(v[x].size()-2)+1;
    if(a==1)return false;
    int oldTime=getTime(v[x]);
    vector<int>tmp=v[x];
    vector<int>vc=fchai(v[x],a);
    vc.insert(vc.begin(),0);
    v[x].push_back(0);
    if(flag==0 && getTime(v[x])+getTime(vc)>=oldTime || flag==1 && expRand((getTime(v[x])+getTime(vc))*1.0/oldTime))
    {
        v[x]=tmp;
        return false;
    }
    v.push_back(vc);
    return getTime(v[x])+getTime(vc)<oldTime;
}
//随机组合
bool merge(vector<vector<int>>&v,int flag)
{
    int x=rand()%v.size(),y=rand()%v.size();
    if(x==y)return false;
    vector<int>vm=join(v[x],v[y]);
    vm.erase(vm.begin()+v[x].size()-1);
    vm.erase(vm.begin()+v[x].size()-1);
    int oldTime=getTime(v[x])+getTime(v[y]);
    if(flag==0 && getTime(vm)>oldTime|| flag==1 && expRand(getTime(vm)*0.1/oldTime) || getSum(vm)>SIZE)
    {
        return false;
    }
    if(x>y)x^=y^=x^=y;
    v.erase(v.begin()+y);
    v.erase(v.begin()+x);
    v.push_back(vm);
    return getTime(vm)<oldTime;
}

//输出方案
void outAns(vector<vector<int>>ans)
{
    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<<")\n";
}

//一轮随机优化,需要4.5秒
vector<vector<int>> calculate2(vector<vector<int>>startAns) 
{
    //srand(time(NULL));
    vector<vector<int>>ans=startAns;
    //outAns(ans);
    for(int i=0;i<3;i++)
    {
        clock_t startTime,endTime;
        startTime = clock();
        while(true)
        {            
            exchange1(ans,0);
            exchange2(ans,0);
            split(ans,0);
            endTime = clock();
            if(endTime-startTime>theTime*1200)break;
        }
        startTime = clock();
        while(true)
        {
            merge(ans,0);
            endTime = clock();
            if(endTime-startTime>theTime*300)break;
        }
    }
    //cout<<endl;
    //cout<<ansTime(ans)<<"   ";
    return ans;
}
//模拟退火,需要9.5秒
void calculate3(vector<vector<int>>&lastAns,vector<vector<int>> ans) 
{
    //outAns(ans);
    clock_t startTime,endTime;
    startTime = clock();
    while(true)
    {            
        exchange1(ans,1);
        exchange2(ans,1);
        split(ans,1);
        merge(ans,1);
        endTime = clock();
        if(endTime-startTime>theTime*5000)break; 
    }
    //cout<<ansTime(ans)<<"   ";
    ans = calculate2(ans);    
    if(ansTime(lastAns)>ansTime(ans))lastAns=ans;
}

#ifdef windows
void test()
{
    srand(4356);
    int n= 60 ;
    cout<<n<<endl;
    for(int i=0;i<=n;i++)
    {
        for(int i=1;i<=n+1;i++)
        {
            cout<<rand()%5000<<" ";
            if(i%10==0)cout<<endl;
        }
        cout<<endl;
    }
    for(int i=1;i<=n;i++)
    {
        cout<<rand()%500<<" ";
        if(i%10==0)cout<<endl;
    }
}
void test2()
{
    srand(3346);
    int n= 100 ;
    int x[101],y[101];
    cout<<n<<endl;
    for(int i=0;i<=n;i++)
    {
        x[i]=rand()%3000,y[i]=rand()%3000;
    }
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=n;j++)
        {
            if(j)cout<<',';
            cout<<(int)sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
            //if(j && j%10==0)cout<<endl;
        }
        cout<<endl;
    }
    for(int i=1;i<=n;i++)
    {
        cout<<rand()%500<<endl;
    }
    //cout<<endl;
    for(int i=0;i<=n;i++)cout<<x[i]<<' '<<y[i]<<endl;
}
void test3()
{
    for(int i=1;i<=50;i++)cout<<"0 "<<i<<" 0 -1 ";
    cout<<endl;
}
void test4()
{
    //srand(time(NULL));
    cout<< rand()<<"  ";
}
#endif

#ifdef windows
int main()
#else
int main(int argc, char *argv[])
#endif
{
#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
    //for(int i=0;i<10;i++)test4();
    std::cout << deliveryFile << " " << answerFile << std::endl;
    FILE *pin = freopen(deliveryFile.c_str(),"r",stdin);
    FILE *pout = freopen(answerFile.c_str(),"w",stdout);

    while(init())
    {
#ifdef TEST
        int lujing =50; //需要硬编码
        jxans.resize(lujing);
        for(int i=0;i<lujing;i++)jxans[i].resize(101);
        for(int i=0;i<lujing;i++)
        {
            for(int j=0;j<101;j++)
            {
                CIN(jxans[i][j]);
                if(jxans[i][j]==-1)
                {
                    jxans[i].resize(j);
                    break;
                }
            }
        }
#endif
        //test2();
        vector<vector<int>>ans,tmp;
        ans=calculate();
        //outAns(ans);
        for(int i=0;i<50;i++)
        {
            //cout<<"calculate2 开始\n";
            tmp = calculate2(calculate());
            if(ansTime(ans)>ansTime(tmp))ans=tmp;
        }      
        for(int i=0;i<100;i++)
        {            
            //cout<<"calculate31\n";
            calculate3(ans,calculate());
        }
        for(int i=0;i<50;i++)
        {
            //cout<<"calculate32\n";
            calculate3(ans,ans);
        }
        for(int i=0;i<50;i++)
        {
            //cout<<"calculate4\n";
            calculate3(ans,calculate4());
        }
        outAns(ans);
    }
    fclose(pin);
    fclose(pout);
    std::cout << "End" << std::endl;
    return NORMAL_CODE;
}

猜你喜欢

转载自blog.csdn.net/nameofcsdn/article/details/107519401