分支限界法求解旅行商问题

问题来来自2016华为软件精英挑战赛。

 问题定义

给定一个带权重的有向图G=(V,E),V为顶点集,E为有向边集,每一条有向边均有一个权重。对于给定的顶点s、t,以及V的子集V’,寻找从s到t的不成环有向路径P,使得P经过V’中所有的顶点(对经过V’中节点的顺序不做要求)。

若不存在这样的有向路径P,则输出无解,程序运行时间越短,则视为结果越优;若存在这样的有向路径P,则输出所得到的路径,路径的权重越小,则视为结果越优,在输出路径权重一样的前提下,程序运行时间越短,则视为结果越优。

说明:

1)图中所有权重均为[1,20]内的整数;

2)任一有向边的起点不等于终点;

3)连接顶点A至顶点B的有向边可能超过一条,其权重可能一样,也可能不一样;

4)该有向图的顶点不会超过600个,每个顶点出度(以该点为起点的有向边的数量)不超过8;

5)V’中元素个数不超过50;

6)从s到t的不成环有向路径P是指,P为由一系列有向边组成的从s至t的有向连通路径,且不允许重复经过任一节点;

7)路径的权重是指所有组成该路径的所有有向边的权重之和。

与经典旅行商问题相比,有几点小区别:

①是有向图

②起点终点是不同的点

③大图中的指定点集,难度更大

由于大赛对程序运行时间有限制,所以需要一个求近似解的算法。

但还是现用分支限界试试,用C++做了初步的实现,暂做记录。

3月7日下午两点半修改了代码,改用链表维护path,节省内存

#pragma once
#include <iostream>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <vector>
#include <stack>
using namespace std;

struct Node {
	int id;
	Node* path_father;	//路径中父节点
	int already;		//path中V'中节点数目(默认source和destination不在V'中)
	int path_cost;
	Node(int i) : id(i), path_cost(0), already(0), path_father(NULL) {};
	Node(int i, int c, Node* p, int a) : id(i), path_cost(c), path_father(p), already(a) {};

	bool inPath(int target) {
		Node* path_rear = this;
		while (path_rear) {
			if (target == path_rear->id)
				return true;
			path_rear = path_rear->path_father;
		}
		return false;
	}
};

//pq中优先级的比较
struct Compare {
	//返回 node1优先级低于node2优先级
	bool operator () (Node*node1, Node*node2) {
		//经过一个V'节点的平均耗费
		if (!node1->already || !node2->already) {
			if (!node1->already)
				return true;
			return false;
		}
		//平均耗费越大,优先级越低
		return node1->path_cost / node1->already > node2->path_cost / node2->already;
	}
};

class Graph {
public:
	//图信息
	unordered_map<int, unordered_map<int, int>> neighbors;//id:<邻居id:最小边长>集合
	unordered_map<int, int> edges;//边id:cost
	unordered_set<int> V;//V'
	int source_id, destination_id;

	//优先队列(待扩展点)
	priority_queue<Node*, vector<Node*>, Compare> pq;

	//所有node地址集合,便于最后释放
	set<int> node_address;

	//最短路径长度初值为极大
	int min_cost = INT_MAX;
	Node* min_path;


	//TODO:初始化
	Graph() {};
	Graph(char* graph[5000], int edge_num, char *condition) { initialize(graph, edge_num, condition); };
	~Graph() {
		for (auto a : node_address) {
			cout << "is going to delete " << ((Node*)a)->id << endl;
			delete (Node*)a;
		}
	};


	//将当前扩展节点node的 不在node->path中的neighbor节点add到pq中
	void addNeighborsToPq(Node* node) {
		//node的每一条出边
		for (auto pair : neighbors[node->id]) {
			int neighbor_id = pair.first, cost = pair.second;//邻居节点、相应cost
			//已在path中,或未遍历完V情况下到了终点
			if (node->inPath(neighbor_id) || (node->already < V.size() && neighbor_id == destination_id))
				continue;

			Node* neighbor_node = new Node(neighbor_id,
										node->path_cost + cost,
										node,	//path上的父亲节点
										V.count(neighbor_id) ? node->already + 1 : node->already);
			node_address.insert((int)&(*neighbor_node));
			//已经过V中全部点 且 该点是终点
			if (neighbor_node->already == V.size() && neighbor_id == destination_id)	 {
				if (neighbor_node->path_cost < min_cost) {
					cout << "change min_cost from " << min_cost;
					min_cost = neighbor_node->path_cost, min_path = neighbor_node;
					cout << " to " << min_cost << endl;
					printPath(min_path);
				}
			}
			else
				pq.push(neighbor_node);
		}
	}

	//取pq的top并将其pop
	Node* priorityPop() {
		Node* result = pq.top();
		pq.pop();
		return result;
	}

	void printPath(Node* path_rear) {
		cout << "path : ";
		stack<int> s;
		while (path_rear) {
			s.push(path_rear->id);
			path_rear = path_rear->path_father;
		}
		while (!s.empty())
			cout << s.top() << " ", s.pop();
		cout << endl;
	}

	void search() {
		cout << "search !!!" << endl;
		Node* source_node = new Node(source_id);
		pq.push(source_node);
		while (!pq.empty()) {
			Node* node = priorityPop();
			if (node->path_cost >= min_cost)
				continue;
			addNeighborsToPq(node);
		}

		cout << endl << endl;
		cout << "min_cost : " << min_cost << endl;
		cout << "min_path : ";
		printPath(min_path);
	}

	void initialize(char *graph[5000], int edge_num, char *condition) {

		for (int i = 0; i < edge_num; i++) {
			char* line = graph[i];
			int index = 0;
			//LinkID
			int l_id = 0;
			while (line[index] != ',')
				l_id = l_id * 10 + line[index++] - '0';
			index++;
			//SourceID
			int s_id = 0;
			while (line[index] != ',')
				s_id = s_id * 10 + line[index++] - '0';
			index++;
			//DestinationID
			int d_id = 0;
			while (line[index] != ',')
				d_id = d_id * 10 + line[index++] - '0';
			index++;
			//Cost
			int c = 0;
			while (line[index])
				c = c * 10 + line[index++] - '0';
			//设置图信息
			edges[l_id] = c;
			neighbors[s_id][d_id] = neighbors[s_id][d_id] == 0 ? c : min(neighbors[s_id][d_id], c);

		}
		int index = 0;
		while (condition[index] != ',')
			source_id = source_id * 10 + condition[index++] - '0';
		index++;
		while (condition[index] != ',')
			destination_id = destination_id * 10 + condition[index++] - '0';
		index++;


		while (condition[index]) {
			int node_id = 0;
			while (condition[index] && condition[index] != '|')
				node_id = node_id * 10 + condition[index++] - '0';
			if (condition[index])
				index++;
			V.insert(node_id);
		}
	}

	void printGraph() {
		cout << "printGraph !!!" << endl;

		cout << "V' : ";
		for (auto node : V)
			cout << node << " | ";
		cout << endl;

		cout << "source_id : " << source_id << endl;
		cout << "destination_id : " << destination_id << endl;

		cout << "edges:" << endl;
		for (auto edge : edges)
			cout << "    LinkID " << edge.first << " : cost " << edge.second << endl;

		cout << "neighbors:" << endl;
		for (auto pair : neighbors)
			for (auto neighbor : pair.second)
				cout << "    source_id " << pair.first << " : destination_id " << neighbor.first << " : shortest_edges_len " << neighbor.second << endl;

		cout << endl;
	}

};

////你要完成的功能总入口
//void search_route(char *graph[5000], int edge_num, char *condition)
//{
//	unsigned short result[] = { 2, 6, 3 };//示例中的一个解
//
//	for (int i = 0; i < 3; i++)
//		record_result(result[i]);
//}

void main() {
	char graph[7][8] =
	{ "0,0,1,1",
	"1,0,2,2",
	"2,0,3,1",
	"3,2,1,3",
	"4,3,1,1",
	"5,2,3,1",
	"6,3,2,1" };

	char* G[5000];
	for (int i = 0; i < 7; i++) {
		G[i] = new char[8];
		for (int j = 0; j < 8; j++)
			G[i][j] = graph[i][j];
	}


	char* condition = "0,1,2|3";

	Graph g(G, 7, condition);
	g.printGraph();
	g.search();
	system("pause");
}

    原文作者:分支限界法
    原文地址: https://blog.csdn.net/hgqqtql/article/details/50811810
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞