问题来来自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");
}