[英]Placing electric stations NP Hard problem
我们有一个问题(据说是 NP Hard)如下:
给定一个连接的无向未加权图 G = (V, E) 和一组从家到办公室的路线(一系列顶点)(可以有多条从家开始的路线,也可以有多条结束于办公室的路线),我们想要放置最少的充电站,使所有路线至少覆盖一次。 我们不能在家里或办公室放置电台。
房子图标是家,砖墙图标是办公室。
上面的最佳方案是将站点放置在顶点R和D处。
寻找最优解被称为 NP Hard 问题。
我有一个方法,它是这样的:
Let S be empty set of charging stations (vertices)
Let R be set of all given routes, r_i = <v_0, v_1 ... v_j> where v_0 is of type "home" and v_j is of type "office"
Let Vdeg be array of integers of size |V| intially set to 0
For r in R:
Increment Vdeg of every vertex v in r
while R is not empty:
Sort vertices by decreasing Vdeg
Let maxDeg be maximum degree vertex in V
Add maxDeg to S
for all routes r in R which contains maxDeg
R = R\{r}
Decrement Vdeg for maxDeg
上面的算法在(我相信)多项式时间内运行,并且(我相信)给出了最优解。
完整的实现如下:
#include <iostream>
#include <vector>
// A Graph Vertice
struct node {
int id;
std::string type;
std::string name;
};
std::ostream& operator <<(std::ostream& out, const node& n) {
return out << "{ " << n.type << ", " << n.name << " }";
}
// A Graph Edge
struct edge {
node * from;
node * to;
int weight;
};
std::ostream& operator <<(std::ostream& out, const edge& e) {
return out << "{ " << *e.from << ", " << *e.to << ", " << e.weight << " }";
}
std::ostream& operator <<(std::ostream& out, const std::vector<edge>& graph) {
out << "{ ";
for(int i = 0; i < graph.size(); ++i) {
out << graph[i] << ", ";
}
return out << "\b\b }";
}
std::ostream& operator <<(std::ostream& out, const std::vector<node*>& vertices) {
out << "{ ";
for(int i = 0; i < vertices.size(); ++i) {
out << "\n " << *vertices[i] << ", ";
}
return out << "\b\b \n}";
}
std::ostream& operator <<(std::ostream& out, const std::vector<edge*>& graph) {
out << "{ ";
for(int i = 0; i < graph.size(); ++i) {
out << "\n " << *graph[i] << ", ";
}
return out << "\b\b \n}";
}
std::ostream& operator <<(std::ostream& out, const std::vector<std::vector<edge*>>& graph) {
out << "{";
for(int i = 0; i < graph.size(); ++i) {
out << "\n " << graph[i] << ", ";
}
return out << "\b\b \n}";
}
// A Vdeg which also stores id and routes intersecting on it
struct vertice_degrees {
int id;
int degree;
std::vector<int> intersecting;
};
// Greater than operator overloaded for sorting Vdegs by degree
bool operator >(const vertice_degrees& vd1, const vertice_degrees& vd2) {
return vd1.degree > vd2.degree;
}
std::ostream& operator <<(std::ostream& out, const std::vector<vertice_degrees>& vd) {
out << "{ ";
for(int i = 0; i < vd.size(); ++i) {
out << "{ " << vd[i].id << ", " << vd[i].degree << ", { ";
for(int j = 0; j < vd[i].intersecting.size(); ++j) {
out << vd[i].intersecting[j] << ", ";
}
out << "\b\b }, ";
}
return out << "\b\b }";
}
void print_routes(const std::string label, const std::vector<int> routes_to_cover, const std::vector<std::vector<edge*>> routes, const std::string label_after) {
std::cout << label << "{ ";
for(int i = 0; i < routes_to_cover.size(); ++i) {
std::cout << routes[routes_to_cover[i]] << ", ";
}
std::cout << "\b\b }" << label_after;
}
int main() {
// List of vertices in graph
node vertices[] = {
node{ 0, "House", "A" },
node{ 1, "House", "B" },
node{ 2, "House", "F" },
node{ 3, "House", "N" },
node{ 4, "House", "S" },
node{ 5, "Office", "H" },
node{ 6, "Office", "K" },
node{ 7, "Office", "L" },
node{ 8, "Office", "P" },
node{ 9, "Office", "T" },
node{ 10, "None", "C" },
node{ 11, "None", "D" },
node{ 12, "None", "E" },
node{ 13, "None", "G" },
node{ 14, "None", "I" },
node{ 15, "None", "J" },
node{ 16, "None", "M" },
node{ 17, "None", "O" },
node{ 18, "None", "Q" },
node{ 19, "None", "R" },
node{ 20, "None", "U" }
};
// Length of vertices array
const int vertice_count = sizeof(vertices)/sizeof(node);
// List of edges in graph
std::vector<edge> graph = {
edge{ &vertices[0], &vertices[12], 3 },
edge{ &vertices[12], &vertices[15], 3 },
edge{ &vertices[15], &vertices[19], 4 },
edge{ &vertices[19], &vertices[20], 5 },
edge{ &vertices[20], &vertices[9], 5 },
edge{ &vertices[1], &vertices[10], 4 },
edge{ &vertices[10], &vertices[11], 2 },
edge{ &vertices[11], &vertices[5], 2 },
edge{ &vertices[2], &vertices[12], 4 },
edge{ &vertices[12], &vertices[14], 4 },
edge{ &vertices[14], &vertices[11], 3 },
edge{ &vertices[11], &vertices[13], 7 },
edge{ &vertices[13], &vertices[6], 2 },
edge{ &vertices[3], &vertices[19], 3 },
edge{ &vertices[19], &vertices[16], 5 },
edge{ &vertices[16], &vertices[17], 2 },
edge{ &vertices[17], &vertices[7], 2 },
edge{ &vertices[4], &vertices[19], 4 },
edge{ &vertices[19], &vertices[17], 6 },
edge{ &vertices[17], &vertices[18], 4 },
edge{ &vertices[18], &vertices[8], 3 }
// Remaining edges are not on routes, and hence are inconsequential and can be omitted
};
// List of routes
std::vector<std::vector<edge*>> routes_arr = {
std::vector<edge*>{ &graph[0], &graph[1], &graph[2], &graph[3], &graph[4] },
std::vector<edge*>{ &graph[5], &graph[6], &graph[7] },
std::vector<edge*>{ &graph[8], &graph[9], &graph[10], &graph[11], &graph[12] },
std::vector<edge*>{ &graph[13], &graph[14], &graph[15], &graph[16] },
std::vector<edge*>{ &graph[17], &graph[18], &graph[19], &graph[20] }
};
std::cout << routes_arr;
// Vdeg list of size V, and initialised
std::vector<vertice_degrees> vertice_degrees_arr;
for(int i = 0; i < vertice_count; ++i) {
vertice_degrees_arr.push_back(vertice_degrees{ i, 0 });
}
// Add all the routes the vertice is part of for each vertice in Vdeg
for(int i = 0; i < routes_arr.size(); ++i) {
for(int j = 0, k; j < routes_arr[i].size(); ++j) {
vertice_degrees& vd = vertice_degrees_arr[routes_arr[i][j]->from->id];
vd.degree += 1;
for(k = 0; k < vd.intersecting.size(); ++k) {
if(vd.intersecting[k] == i) {
continue;
}
}
vd.intersecting.push_back(i);
}
}
// routes_to_cover is remaining routes left to cover, list of route indexes
std::cout << "\nFilled: \n" << vertice_degrees_arr;
std::vector<int> routes_to_cover;
for(int i = 0; i < routes_arr.size(); ++i) {
routes_to_cover.push_back(i);
}
// Final resulting S
std::vector<node*> stations_placed_at;
// While there are routes still to cover
while(routes_to_cover.size() > 0) {
// Insertion Sort the Vdeg by degree
for(int i = 1, j; i < vertice_degrees_arr.size(); ++i) {
const vertice_degrees vd = vertice_degrees_arr[i];
for(j = i; j > 0 && vertice_degrees_arr[j-1] > vd; --j) {
vertice_degrees_arr[j] = vertice_degrees_arr[j-1];
}
vertice_degrees_arr[j] = vd;
}
// Get all the routes intersecting at v, add v to S
std::vector<int> covered_routes = vertice_degrees_arr[vertice_degrees_arr.size()-1].intersecting;
stations_placed_at.push_back(&vertices[ vertice_degrees_arr[vertice_degrees_arr.size()-1].id ]);
std::cout << "\nSorted: \n" << vertice_degrees_arr;
// Remove routes intersecting at v from all Vdeg, decrease degree for next iteration
for(int i = 0; i < vertice_degrees_arr.size(); ++i) {
for(int j = 0; j < vertice_degrees_arr[i].intersecting.size(); ++j) {
for(int k = 0; k < covered_routes.size(); ++k)
if(vertice_degrees_arr[i].intersecting[j] == covered_routes[k]) {
vertice_degrees_arr[i].intersecting.erase(vertice_degrees_arr[i].intersecting.begin() + j--);
vertice_degrees_arr[i].degree -= 1;
break;
}
}
}
std::cout << "\nCleaned:\n" << vertice_degrees_arr;
// Remove covered routes this iteration from routes_to_cover
for(int i = 0; i < routes_to_cover.size(); ++i) {
for(int j = 0; j < covered_routes.size(); ++j) {
if(routes_to_cover[i] == covered_routes[j]) {
routes_to_cover.erase(routes_to_cover.begin() + i--);
break;
}
}
}
print_routes("\nRemaining routes: ", routes_to_cover, routes_arr, "\n");
}
std::cout << "\nFinal Nodes: " << stations_placed_at;
return 0;
}
那么这个解决方案有什么问题呢?
是吗:
如果是前者,你能解释一下吗?
如果是后者,你能提供一个柜台吗?
您的算法不是最优的,从exact 3-cover problem的减少可以看出。
为了解这一点,让我们从 3 的倍数的住宅数量开始。添加一个办公室。 对于封面中的每个 3 组,我们添加一个顶点,其中包含从 3 组中的 3 个房屋到该顶点,然后到办公室的路线。 很容易验证,如果存在精确的 3-cover,则与该精确的 3-cover 关联的顶点集是加油站的最优排列。 所以你的问题需要能够找到精确的 3-covers。
但是您的算法是贪婪的,它的第一个选择是随机猜测,无法确定任何顶点都比其他顶点好。 如果它首先猜测一个不在任何精确 3-cover 中的 3-set,那么您将找不到最优解。
好的,在阅读了Exact 3 set cover 问题和@btilly的有用答案之后,我终于能够想出一个反例来解决我的算法。
直觉是该算法适用于不同程度的输入。 因此,尝试获得相同的学位。
计数器如下。
给出下图:
通过以下路线:
R1: A, C, F, J, L
R2: B, C, D
R3: E, F, H, K
R4: I, J, H, G
最优选择顶点C和H像这样:
但是,如果我们的算法首先选择F (因为度数相同),如下所示:
然后我们被迫选择C和H或J之一,如下所示:
这显然不是最佳解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.