一,比赛结果
比赛今天截止,最终只得了83分左右(稍微有点点波动),只最后一名多0.07分,多少有些辜负队友的期望,本来是打着要进入32强的旗号比赛的。厉害的人真多啊,特别是最终的排行榜上绝大部分都是成电的,身处同一个环境差距还是有的,你们是我前进的动力!
只有83分的原因在于未能解决最后一个分值较高的高级案例(不过在论坛上看见不少小伙伴卡在这两个问题上), 后来听说官方对测试案例11,14,15专门人工特殊处理了一下,就是为了刷掉那些low算法,刷掉那些不会优化的小伙伴,哎,我就是其中之一啊。
尽管华为比赛没能进入32强,但是还是学到了不少东西。在这里分享一下针对华为前五个低级案例的解决方案,当然我相信有很多同学前五个案例也是这种解决方法
二,比赛感受
1,强者如云
对于比赛提出的这个NP问题实际上本身难度还是比较大的,原因在于为了能找到最优解几乎不可能在多项式时间内解决问题,同时即使你能找到最优解你都无法证明他是最优解!一开始对问题有些措手不及,只知道一味的寻求资源来解决问题,到后来看着大神们尽然那么多那么早就获得了高分,特别是当自己最初的算法设计被证实有问题时感觉好无助。从本次比赛的过程中其实最大的感受就是强者太多,山外总是有山,人外总是有人啊,自己永远也不会是最牛的,永远都不要骄傲。
本次比赛所有的代码都是亲自手写,从悄悄关注别人各种动态的过程中,知道了有一种神器叫“求解器”,原来其实就是调用库解决问题的方式!自己后来也想通过别人口中的求解器解决问题,但是发现基于求解器也是需要很强的建模能力的,,无论哪种解决问题的算法或者方法都需要一定能力!
在比赛途中耳濡目染,交流过程中,已知可解决NP问题的算法有
1),遗传算法
2),蛙跳算法
3),梦卡罗特随机算法,每次运行的结果都不会一样。
4),模拟退火
5),将问题转化成TSP旅行商问题,因为TSP问题和本问题很像,所以有人努力往此问题转化。
6),线性规划
本次比赛还有一个感受就是,实现细节很重要,还有就是把程序写出来的速度很重要,因为后期才来得及对问题各种调试,反复调试!
三,算法设计思想
针对前五个低级案例的解决方案:
1,从起点开始深度优先搜索,并用数组记录沿途边号,当遇到终点时,此时就已经搜索到一条路径,否则就一直深度优先访问下去。
2,这条路径不一定全部经过毕竟点,所以要检查一下是否包含全部必经点。
3,如果全部包含必经点,则存储该路径,以后遇到比该路径权值更小的就更新路径。
4,显然这是一个递归的思路。
四,算法分析
这是一个暴力搜索,从起点到终点的路径随着顶点个数的增多有趋近指数级的条数,并且还要搜索出很多无效路径,所以相当费时。
五,代码的实现:
注意:
1),采用邻接矩阵建图,矩阵中每个元素存储着边的索引号和
class Vertex //顶点的自定义(可能叫边的定义更好点)
{
public:
Vertex()
{
LinkID = -1;//没有相连的索引号
Cost = MAX_WEIGHT;
}
int LinkID;
int Cost;
};
Vertex graph_matrix[MAX_VERTEX_NUMS][MAX_VERTEX_NUMS];//图数据,注意他是个全局变量
2)头文件中类的完整声明
其中图是一个全局变量(邻接矩阵)!
#ifndef __ROUTE_H__
#define __ROUTE_H__
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <map>
#include <iostream>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <functional>
#include <time.h>
using namespace std;
void search_route(char *graph[5000], int edge_num, char *condition);
///updata 2016/04/03
/*以下为自定义函数****/
namespace cf
{
#define MAX_VERTEX_NUMS 600//最大顶点数目
#define MAX_WEIGHT -1//用-1代表最大边权值
#define MAX_MUST_PASS 50
class CodeCraft
{
public:
CodeCraft(int edge_num){
m_vertexNums = 0;
m_mustSize = 0;
m_edgeNums = edge_num;
m_pathSize = 0;
m_minCost = 99999;
m_countAns=0;//统计获得了多少条有效路径
flag_stop = false;
isbadgraph = false;
pMinPath = new int[MAX_VERTEX_NUMS];
for (int i = 0; i < MAX_MUST_PASS; i++)
pMinPath[i] = 0;
pMustPass = new int[MAX_MUST_PASS];
for (int i = 0; i < MAX_MUST_PASS; i++)
pMustPass[i] = 0;
};
~CodeCraft(){
delete[] pMinPath;
pMinPath = NULL;
delete[] pMustPass;
pMustPass = NULL;
};
//建立图模型,邻接矩阵
void build_graph(char ** const topo, const int edge_num);
void edge_to_num(char *one_edge, int *num);
void demand_to_num(char * const demand);
//两种策略
void senior_solution();//较大时的解决方案:寻找近似解
void primary_solution();//规模较小时的解决方案:寻找最优解
//核心函数:深度优先暴力穷举
void dfs_best_paths(int pre_cost, int start, int *visited);
//核心函数:优化的暴力穷举
void force_valid_paths(int curStart, int *result, int idx, vector<int> visited_ban, int preCost);
//核心函数的辅助函数
int next_adj_vertex(int curVex, int nextVex, bool flagfirst);
int get_unvisited_size(vector<int> &visited_ban);
void get_new_path(int *pResultPath, int path_size, int curCost);
int dijkstra(int start_must, int *path, vector<int> &visited_ban, int *shortcost);
//当前源点是否可达其他未访问的必经点,即shortcost中的最短路径值是有效的。
bool is_valid_connect(vector<int> &visited_ban, int *shortcost);
void map_must_vertex();
int unconnected_size();
public:
int m_startVex;//起点
int m_endVex;//终点
int m_vertexNums;//顶点个数,
int m_edgeNums;//边的条数
int m_mustSize;//必经点个数
int m_pathSize;//结果路径的长度
int *pMinPath;//存储最小路径
int m_minCost;//最优路径的权值
int m_countAns;//统计获得了多少条有效路径
bool flag_stop;//终止标志
int *pMustPass;//存放必经点
map<int, int> mapping;
bool isbadgraph;
};
}
#endif
3),深度优先暴力搜索满足条件的所有路径,结果只保存其中最短的一条
具体代码如下:
void CodeCraft::primary_solution()//规模较小时的解决方案:寻找最优解
{
int visited[MAX_VERTEX_NUMS] = { 0 }; //已访问过的顶点
int cur_cost = 0;
dfs_best_paths(cur_cost, this->m_startVex, visited);//开始递归寻找
//记录答案
for (int i = 0; i < this->m_pathSize; i++)
record_result(pMinPath[i]);
}
//妈了个蛋,暴力出奇迹,暴力搜索执行函数
void CodeCraft::dfs_best_paths(int pre_cost, int start, int *visited)
{
int i = 0;
static int path_size = 0;
static int cur_cost = 0;//当前路径代价
static int path_count = 0;//当前符合要求的路径条数
static int path_stack[2000] = { 0 };
visited[start] = 1;
if (start == this->m_endVex){//如果是终点
i = 0;
bool flag = true;
while (i < m_mustSize )//检查是否全部必经点都访问过
{
if (visited[this->pMustPass[i]] != 1)
{
flag = false;//没有经过所有必经点
break;
}
i++;
}
if (flag && this->m_minCost > cur_cost)//必经点全部都访问过,并且当前路径权值更小,就记录路径
{
this->get_new_path(path_stack, path_size, cur_cost);//更新最短路径
path_count++;
}
}
else{
int nextVex = 0,curVex = start;
bool flagfirst = true;
while (nextVex != -1)//如果存在相邻点
{
nextVex = next_adj_vertex(curVex, nextVex, flagfirst);//curVex相邻的下一个顶点
if (nextVex != -1 && visited[nextVex] == 0)//如果该相邻的顶点没有被访问过
{
path_stack[path_size++] = graph_matrix[curVex][nextVex].LinkID;
cur_cost += graph_matrix[curVex][nextVex].Cost;
dfs_best_paths(graph_matrix[curVex][nextVex].Cost, nextVex, visited);
}
flagfirst = false;
}
}
path_size--;
cur_cost -= pre_cost;
visited[start] = 0;
}
//在邻接矩阵从curVex行,第nextVex列开始找curVex的相邻点,即graph_matrix[curVex][i].Cost != -1的点
int CodeCraft::next_adj_vertex(int curVex, int nextVex, bool flagfirst)
{
int i = nextVex;
if (!flagfirst)
i++;
for (; i < m_vertexNums; i++)
{
if (graph_matrix[curVex][i].Cost != -1)
return i;
}
return -1;
}