繁体   English   中英

配电站 NP Hard 问题

[英]Placing electric stations NP Hard problem

我们有一个问题(据说是 NP Hard)如下:

给定一个连接的无向未加权图 G = (V, E) 和一组从家到办公室的路线(一系列顶点)(可以有多条从家开始的路线,也可以有多条结束于办公室的路线),我们想要放置最少的充电站,使所有路线至少覆盖一次。 我们不能在家里或办公室放置电台。

下图显示了一个可能的输入(请忽略权重): 车站输入

房子图标是家,砖墙图标是办公室。

上面的最佳方案是将站点放置在顶点RD处。

寻找最优解被称为 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;
}

那么这个解决方案有什么问题呢?

是吗:

  1. 不是多项式? 或者
  2. 不是最优的?

如果是前者,你能解释一下吗?

如果是后者,你能提供一个柜台吗?

您的算法不是最优的,从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

最优选择顶点CH像这样:

最优解

但是,如果我们的算法首先选择F (因为度数相同),如下所示:

贪心解 Pt1

然后我们被迫选择CHJ之一,如下所示:

贪心解 Pt2

这显然不是最佳解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM