2016华为软件精英挑战赛:低级案例---暴力出奇迹(一)


一,比赛结果

比赛今天截止,最终只得了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;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值