踉踉跄跄进入复赛,复赛题目出来的那一天,结点数最多10000,边最多10000.感觉我的费用流肯定要崩了啊;都有点想弃赛的冲动;
但是缓了几天之后,还是准备试一试,不能给学校丢人啊,到最后一个0分多尴尬;
复赛最大的变化就是服务器有了带宽上限,这点其实不难解决,计算费用流的时候每次加服务器不再提供无限流聊,提供最大档的流量不就可以了,但是这么做出来的结果cost一直很大;改了好久都没有解决;
还有遇到的参赛以来最大的困难就是输出路径那一块了,初赛的时候也听说好几个人输出路径有问题,搞了好几天,我的这时候也爆发了;反正是真真找了两天的问题,一直都是初中级有分,高级路径错误,期间好几次都想弃赛了;
最后才搞出来,是因为服务器结点的带宽不是无限的了,之前是无限的话可以到服务器结点就结束返回,但是有限的话,这个服务器点的带宽输出能力被占满后,还是有可能途径这个点从其他服务器读取流量。真是醉了;
路径解决后就开始漫长优化之路,但是一直都没有什么起色;
后来优化了初始解:使用%20度最好的点+一遍zkw,然后把没有满足的消费节点全部直连满足了,这样的话可以得到一个很不错的初始解;
后来发现交叉根本没什么用,就直接放弃遗传了,直接改了模拟退火;然后效果还是不怎么样;
后来在群里听到有人手动压档,把服务器不舍为最大挡,我试了下,效果出奇的好,初中级都可以逼近最优了(还是有点差距),但是由于费用流的关系,高级始终怼不过大佬们;
后来发现模拟退火也用处不大了,就干脆用个固定算法了;用初始解+(删点+对最好的换+相邻结点换)循环,初级效果稍微好点,高级一个相邻结点交换都跑不完;
最后成绩止步在了西北赛区第十七名,就这样了,总体来说还是一次很不错的经历,从中学习到了很多东西,也激发了我参加其他比赛的兴趣,哈哈;
放上最后的代码,有什么问题可以交流:
#include "deploy.h" #include <stdio.h> #include <memory.h> #include <stdlib.h> #include <time.h> #include <math.h> #include <queue> #include "timerclock.h" #include <vector> #include <iostream> #include "lib_time.h" using namespace std; std::time_t Timer::_time_start; #define MAXLOOP 20000 //遗传最大循环次数 #define OUTLOOP 5000 //多少次最优解不变退出 #define maxcrossp 0.1 //交叉概率 #define maxmp 0.9 //变异概率 #define mincrossp 0.3 //最小交叉概率 #define minmp 0.00001 //最小变异概率 #define NUMEDGE 50000 //原图的最大边数 #define MAXNE 1206 //定义最大点数,初始化数组用 int SUM = 2; //初始化种群个数 int MAXN = 1204; //初始化最大点数,随读取图的大小变化 const int MAXM = 2000000; //定义最大残缺网络边数 const int INF = 1000000000; int S ; //超级源点,连接在消费节点上 int T ; //超级汇点,连接在服务器上 int result_unchange_time; //记录在error前提下最优值未改变的循环次数 int NE; //边的数量 int head[MAXNE]; //以某一个结点为起头的是那一条边 int pp[MAXNE]; //存储前驱顶点 int numserver = 0; //服务器数量 int numedge = 0; //边数 int numnode = 0; //结点数 int numcons = 0; //消费结点数 int servercost = 0; //安装每台服务器花费 char ret[400000]; //保存输出字符串 int vis[MAXNE]; //访问标记 int MIN_COST; //保存当前最小费用 int relasum[MAXNE]; //按出度分割的数组,用于初始化解 int Deploymentcosts[MAXNE]; //该网络节点对应安置服务器的费用 int maxdeploycost; //最大档次服务器费用,每次设服务器都加最大档 int numdeploy; //当前组合下服务器的数量 int deploynode[MAXNE]; //当前的服务器组合 int deploygrade[MAXNE]; //网络节点对应的档次 int constonet[500]; //消费节点直连的网络节点 int nettocons[MAXNE]; //网络节点对应的消费节点 int numrecover = NUMEDGE; //用原图恢复残缺图的边数 bool noway[MAXNE]; //该点只与一个网络节点相连,且不是直连节点,就不考虑该点 bool isconstonet[MAXNE]; //是不是直连 vector<vector<int>> retpath; //保存输出路径 vector<int > tempway; //临时数组,深搜中保存当前路径 vector<int > costway; //保存该路径对应带宽 /*******************************服务器档次结构****************************************/ struct servergrade{ int out; //服务器档次对应最大输出; int hardwarecost; //服务器档次对应的价格 }grade[20]; //初始化服务器档次结构 /*******************************结点的度排序结构**************************************/ struct node{// float sum; //每个网络节点的单位价钱出度 bool havedep; //是否放置了服务器 }point[MAXNE]; //定义每个节点的出度{带宽/cost}和是否有服务器, /*******************************图的邻接表表示****************************************/ struct Edge { int u; //起点 int v; //终点 int cap; //带宽 int cost; //边上的单位带宽费用 int next; //该起点的下一条边 }edge[NUMEDGE],incomedge[MAXM]; //edge为原图,incomedge为残缺图,在残缺图上跑费用流 /***************************************基因结构体************************************/ struct gen{ bool info[MAXNE]; //染色体结构,用一个数组作为染色体编码 int cost; //此染色体结构的花费 float fitfx; //使用度函数值 }; struct gen gen_group[50]; //定义一个含有50个染色体的组 struct gen gen_new[50]; //交叉变异产生的新染色体 struct gen gen_result; //记录最优的染色体 struct gen gen_now; //记录最优的染色体 /**************************************基础函数声明**********************************/ void recover(); //用原图恢复残缺图 void init(); //初始化 void ways(int f,int u); //深搜求路径 void reading(char * topo[MAX_EDGE_NUM]); //读取数据 void addedge(int u,int v,int cap,int cost); //个位的0、1代表一对正反向的边 void deledge(int u,int v); //个位的0、1代表一对正反向的边 void character(int temp,int& i); //输出数字转换成char数组 int getcost(int ans); //计算当前组合的费用 char* output(); //将路径数组转为char* int relativerand(int *div ,int left, int right); //按相对权值返回一个随机值 bool isright(); //当前组合是不是一个正确解 int randbit(int i,int j); //产生一个在i,j两个数之间的随机整数 int randsign(float p); //按概率p返回1 void delser(int aaa); void deldeploy(int u); //在保存服务器组合的数组中删除服务器点 void adddeploy(int u); //往当前服务器组合中加点 void deleteone(); //删除某一个服务器是否可行 void changebyout(); //按照度交换服务器点 void changebyout2(); void changebyneighbor(); //邻域度交换服务器点 void changebyneighbor2(); /**************************************遗传操作声明**********************************/ void initiate(); //初始化函数,主要负责产生初始化种群 void optimi( int flag ); //评估种群中各染色体的适应度,并据此进行排序 void gen_swap(gen *a,gen *b); //结构体交换 void gen_quicksort(gen *number,int left,int right); //种群排序 void cross(int n); //交叉函数 void selection(); //轮盘赌选择函数 void mutation(); //变异函数 int record(int n); //记录每次循环产生的最优解并判断是否终止循环 int ANS,TEMPCOST; //ANS为返回的总cost int aug (int u,int f) { if (u == T) { ANS += TEMPCOST * f; //累加路径上的花费 return f; } vis[u] = true; //如果该点不是终点,则从该点开始继续递归找路径 int tmp = f , delta , i ; for (i = head[u]; i != -1; i = incomedge[i].next){ if ( incomedge[i].cap && !incomedge[i].cost && !vis[incomedge[i].v]) { delta = aug(incomedge[i].v, tmp < incomedge[i].cap ? tmp : incomedge[i].cap); incomedge[i].cap -= delta; incomedge[i^1].cap += delta; tmp -= delta; if (!tmp) return f; } } return f-tmp; } bool modlabel() { int u , i , tmp; static int dist[MAXNE]; //存储到源点s的距离 for( i = 0 ; i < MAXN ; ++i ) dist[i] = INF ; dist[T] = 0; static deque<int> Q; Q.push_back(T); while(!Q.empty()) { u = Q.front(); Q.pop_front(); for(i = head[u]; i != -1; i = incomedge[i].next) if(incomedge[i^1].cap && (tmp = dist[u] - incomedge[i].cost) < dist[incomedge[i].v]) (dist[incomedge[i].v] = tmp) <= dist[Q.empty() ? S : Q.front()] ? Q.push_front(incomedge[i].v) : Q.push_back(incomedge[i].v); } for(u = 0; u < MAXN; ++u) for(i = head[u]; i != -1; i = incomedge[i].next) incomedge[i].cost += dist[incomedge[i].v] - dist[u]; TEMPCOST += dist[S]; return dist[S] < INF; } void ZKW() { ANS=TEMPCOST=0; while(modlabel()) { do { //memset(vis, 0, sizeof(vis)); for( int i = 0; i < MAXN ; ++i) vis[i] = 0 ; }while(aug(S, INF)); } } void deploy_server(char * topo[MAX_EDGE_NUM], int line_num,char * filename){ Timer::tic(); int i , k , ptr; result_unchange_time = 0; gen_result.cost = INF; init(); //初始化 reading(topo); //读图 srand((unsigned)time(NULL)); //设置随机种子 initiate(); //产生初始化种群,未经优化的初始种群 numdeploy = 0; ptr = head[T]; //断开所有与超级汇点相连的网络节点,即服务器 while(ptr != -1){ numdeploy++; ptr = incomedge[ptr].next; } recover(); //恢复残缺图 ZKW(); //求费用流 int coss = getcost(ANS); //计算安装花费 i = head[S]; //深搜求出路径矩阵 while(i != -1){ ways(edge[i].cap , edge[i].v); i = edge[i].next; } char *topo_file = output(); //将路径矩阵转换成char* write_result(topo_file, filename); } int relativerand(int *div ,int left, int right){ //以div数组在left和right中选出一个点,div是一个0到32768之间取值的数组 int randtemp = randbit(0 , 32768); int mid; while(left <= right){ mid = (left + right)/2; if(div[mid] < randtemp){ left = mid + 1; } else{ right = mid - 1; } } return right; } void initiate(){ int i ,ptrT ; ptrT = head[T]; while(ptrT != -1){ deledge(T , incomedge[ptrT].v); ptrT = incomedge[ptrT].next; } numdeploy = 0; //@************初始解0********************************** //因为在读取数据是已经把服务器连接到消费节点直接相邻的网络节点上,所以现在直接读取 for(i = 0; i < numnode ; ++i){ gen_result.info[i] = false; gen_result.info[i] = false; gen_now.info[i] = false; } gen_group[0].cost = INF; for( i = 0 ; i < 0.2 * numcons ; ++i){ int dey = 0; while(point[dey].havedep || noway[dey]){ ++dey; } for(int j = dey+1; j < numnode ; ++j){ if(!point[j].havedep && point[j].sum > point[dey].sum && !noway[dey]){ dey = j; } } addedge( dey , T , maxdeploycost , 0 ); adddeploy(dey); point[dey].havedep = true; } recover(); ZKW(); ptrT = head[S]; while(ptrT != -1){ if(incomedge[ptrT].cap != 0 && !point[incomedge[ptrT].v].havedep){ addedge( incomedge[ptrT].v , T , maxdeploycost , 0 ); adddeploy(incomedge[ptrT].v); point[incomedge[ptrT].v].havedep = true; } ptrT = incomedge[ptrT].next; } recover(); ZKW(); MIN_COST = getcost(ANS);//计算直连花费 deleteone(); if(numnode < 800){ while(!Timer::timeout()){ changebyout(); changebyneighbor(); deleteone(); changebyneighbor2(); changebyout2(); deleteone(); } } else{ changebyout(); changebyneighbor(); deleteone(); } recover(); ZKW(); } void reading(char * topo[MAX_EDGE_NUM]){ int numnodeedge[MAXN]; memset(numnodeedge ,0 ,sizeof(numnodeedge) ); int temp[3] = {0}; int i = 0,k = 0,j = 0, tem; int link[4]; while(topo[0][i] != ' '){ if(topo[0][i] >= '0' && topo[0][i] <= '9') temp[k] = temp[k]*10 + topo[0][i] - '0'; else{ ++k; } ++i; } numnode = temp[0]; numedge = temp[1]; numcons = temp[2]; MAXN = numnode + 4; S = numnode + 2; T = numnode + 1 ; int dangwei = 0; for(i = 2 ; topo[i][0] != ' ' && topo[i][0] != ' ' ; ++i){ tem = 0; k = 0; j = 0; while(topo[i][j] != ' '&&topo[i][j] != '